[Python] Python学习笔记:面向对象

什么是对象

对象的基本概念

Python中的所有的事物都是以对象形式存在,从简单的数值类型到复杂的代码模块都是对象
对象=属性+方法:对象以id作为标识,既包括数据(属性),也包括代码(方法),是某一类具体事物的特殊实例

# 在python concole中输入,为区分输入输出加了>>>
>>>id(1)  # 整数1这个对象保存的地址
140711440851344
>>>type(1)  # 类型
<class 'int'>
>>>dir(1)  # 查看属性和方法
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

函数也是对象

>>>id(abs)
1795808880264
>>>type(abs)
<class 'builtin_function_or_method'>  # 内置的函数或者方法
>>>dir(abs)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']

创建对象

对象是类的实例,是程序的基本单元。

要创建一个新的对象,首先必须定义一个类,用以证明该类型的对象所包含的内容(属性和方法)。

同一类(class)的对象具有相同的属性和方法,但属性值和id不同。

对象的名称:赋值语句给予对象名称,对象可以有多个名称(变量引用),但只有一个id
例:a=complex(1,2)
b=a \quad # b也指向complex(1,2)这个对象

对象实现了属性和方法的封装,是一种数据抽象机制

对象属性和方法的引用

引用形式:<对象名>.<属性名>
可以跟一般的变量一样用在赋值语句和表达式中
例:“abc”.upper() 变成全大写 "abc"为字符串对象w
(1+2j).real
(1+2j).imag

Python语言动态的特征,使得对象可以随时增加或者删除属性或者方法 del f3.fz

扫描二维码关注公众号,回复: 11647049 查看本文章

面向对象编程

面向对象编程(OOP)是一种程序设计范型,同时也是一种程序开发方法。

程序中包含各种独立而又能 互相调用的对象。
每个对象都能接受、处理数据并将数据传递给其他对象。

类的定义与调用

什么是类

类(class)是对象的模板,封装了对应现实实体的性质和行为。
实例对象是类的具体化。
把类比作模具,对象则是用模具制造出来的零件。

类的出现,为面向对象编程的三个最重要的特性提供了实现的手段:封装性、继承性、多态性。
和函数相似,类是一系列代码的封装。
Python中约定,类名用大写字母开头函数用小写字母开头,以便区分。

定义类

class语句:

class <类名><一系列方法的调用>

类的初始化

class <类名>def __init__(self, <参数表>)def <方法名>(self, <参数表>):

__init__() 是一个特殊的函数名,用于根据类的定义创建实例对象,第一个参数必须是self

调用类

<类名>(<参数>)
调用类会创建一个对象,(注意括号
obj = <类名>(<参数名>)
返回一个对象实例
类方法中的self指这个对象实例本身

使用点(.)操作符来调用对象里的方法
t = turtle.Pen() # 生成海龟
t.forward(100) # 这里的t指向对象实例的变量
t.self(90)

class Force:  # 力
    def __init__(self, x, y):  # x,y方向变量
        self.fx, self.fy = x, y

    def show(self):
        print("Force<%s,%s>" % (self.fx, self.fy))

    def add(self, force2):  # 与另一个力合成
        x = self.fx + force2.fx
        y = self.fy + force2.fy
        return Force(x, y)


# 生成一个力对象
f1 = Force(0, 1)
f1.show()

# 生成另一个力对象
f2 = Force(3, 4)
# 合成为新的力
f3 = f1.add(f2)
f3.show()

结果为:

Force<0,1>
Force<3,5>

类定义中的特殊方法

基本概念

特殊方法:在类定义中实现的一些特殊方法,可以方便地使用Python的一些内置函数。
所有特殊方法的名称以两个下划线(__)开始和结束。

构造与解构

对象构造器: __init__(self,[…) 对象的构造器,实例化对象时调用
析构器:__del__(self,[…) 销毁对象时使用

算术运算

算术操作符
__add__(self,other): 使用 + 操作符
__sub__(self,other): 使用 - 操作符
__mul__(self,other): 使用 * 操作符
__siv__(self,other): 使用 / 操作符

反运算:当左操作数不支持相应的操作时被调用
__radd__(self,other)
__rsub__(self,other)
__rmul__(self,other)
__rdiv__(self,other)

比较大小
__eq__(self,other): 使用 == 操作符
__ne__(self,other): 使用 != 操作符
__lt__(self,other): 使用 < 操作符
__gt__(self,other): 使用 > 操作符
__le__(self,other): 使用 <= 操作符
__ge__(self,other): 使用 >= 操作符

在类的定义后边加上:

    __add__ = add

    def __str__(self):
        return "F<%s,%s>" % (self.fx, self.fy)

    def __mul__(self, n):
        x, y = self.fx * n, self.fy * n
        return Force(x, y)

    def __eq__(self, force2):
        return (self.fx == force2.fx) and (self.fy == force2.fy)

结果为:

Fadd=F<3,5>
Fmul=F<0.0,4.5>
F<0,1>==F<3,4>? -> False

其他特殊方法

字符串操作:
不仅数字类型可以使用像 +(__add__())和 - (__sub__()) 的数学运算符,例如字符串类型可以使用+进行拼接,使用* 进行复制

__str__(self):自动返回字符串
__repr__(self):返回一个用来表示对象的字符串
__len__(self):返回元素个数

自定义对象的排序

列表排序

列表方法sort()
对原列表进行排序,改变原列表的内容
若元素为字符串,则按照字母表顺序排列

# 在python concole中输入,为区分输入输出加了>>>
>>>num = [4,2,7,0,1]
>>>num.sort()  # 默认升序
>>>num
[0, 1, 2, 4, 7]
>>>num.sort(reverse=True)  # 添加参数可改为降序
>>>num
[7, 4, 2, 1, 0]

# 字符串
>>>name = ['John', 'Connor', 'Bruce', 'Arthur', 'Edward']
>>>name.sort()
>>>name
['Arthur', 'Bruce', 'Connor', 'Edward', 'John']

内置排序函数

通用函数sorted()
类似sort(),但返回的是排好序的列表副本,不改变原列表的内容
只有当列表中所有元素都是同一种类型时,sort()和sorted()才会正常工作

>>>name = ['John', 'Connor', 'Bruce', 'Arthur', 'Edward']
>>>sorted_name = sorted(name)
>>>sorted_name
['Arthur', 'Bruce', 'Connor', 'Edward', 'John']
>>>name
['John', 'Connor', 'Bruce', 'Arthur', 'Edward']

特殊方法

特殊方法__lt__ (<)
由于Python的可扩展性,每种数据类型可以定义特殊方法 def __lt__ (self,y) 返回 True 视为比y小,排在前;返回False 视为比y大,排在后

只要类定义中定义了特殊方法__lt__ ,任何自定义类都可以使用 x<y这样的比较

可扩展的“大小”比较及排序

例子:Student 姓名name,成绩grade

按照成绩由高到低排序

class Student:
    def __init__(self, name, grade):
        self.name, self.grade = name, grade

    # 内置sort函数只引用 < 比较符判断前后,那我们就定义一个__lt__特殊方法,这样就可以使用x<y这样的比较
    def __lt__(self, other):
        # 成绩比other高的,排在前面
        return self.grade > other.grade

    # 下边这两个,哪一个不加都会导致打印出来的是一个地址,不可读
    # Student的易读字符串表示
    def __str__(self):
        return "(%s,%d)" % (self.name, self.grade)

    # Student的正式字符串表示,让其与易读字符串相同
    __repr__ = __str__

注意:对于定于的__lt__ 方法,为什么return self.grade > other.grade 使用大于号,在b站老师的直播答疑课上是这样说的:比较谁的体重轻,轻的排在前,用小于号(self<other,返回True,self排在前,self更轻);比较谁的成绩好,成绩好的排在前面,用大于号(self>other,返回True,self排在前,self成绩更好)。大于小于号主要看谁排在前面。主要返回True,就排在前面。

构造一个Python List对象

# 构造一个Python List对象
s = list()
# 添加Student对象到List中
s.append(Student("Jack", 80))
s.append(Student("Jane", 75))
s.append(Student("Smith", 82))
s.append(Student("Cook", 90))
s.append(Student("Tom", 70))
print("Original:", s)

# 对List进行排序,使用内置sort方法,即调用了上面定义的__lt__特殊方法 
s.sort()

# 查看结果,已经按照成绩排好序
print("Sorted:", s)

结果为:

Original: [(Jack,80), (Jane,75), (Smith,82), (Cook,90), (Tom,70)]
Sorted: [(Cook,90), (Smith,82), (Jack,80), (Jane,75), (Tom,70)]

print(s[0] < s[1]) 的结果为True
意思是:s[0]比s[1]成绩好,排在前

直接调用列表sort方法:可以根据__lt__ 定义排序
直接比较Student对象的大小:s[i]<s[j]
还可以定义其他比较符:__gt__等

重新定义__lt__ 方法,改为比较姓名
只需在上面的代码基础上改__lt__ 方法,就可使sort方法按照姓名来排序

    def __lt__(self, other):
        # 姓名字母顺序在前,排在前面
        return self.name < other.name

结果为:

Original: [(Jack,80), (Jane,75), (Smith,82), (Cook,90), (Tom,70)]
Sorted: [(Cook,90), (Jack,80), (Jane,75), (Smith,82), (Tom,70)]

类的继承

类的继承机制

继承:如果一个子类A继承自另一个子类B,就把继承者A称为子类,被继承的类B称为父类基类超类

代码复用:
利用继承可以从已有类中衍生出新的类,添加或修改部分功能
新类具有旧类中的各种属性和方法,而不需要进行任何复制

class Car:
    def __init__(self, name):
        self.name = name
        self.remain_mile = 0

    def fill_fuel(self, miles):  # 加燃料里程
        self.remain_mile = miles

    def run(self, miles):  # 跑miles英里
        print(self.name, end=':')
        if self.remain_mile >= miles:
            self.remain_mile -= miles
            print("run %d miles!" % miles)
        else:
            print("fuel out!")


# 定义子类
class GasCar(Car):  # 汽油车
    def fill_fuel(self, gas):  # 加汽油gas升
        self.remain_mile = gas * 6.0  # 每升跑6英里


class EleCar(Car):  # 电车
    def fill_fuel(self, power):  # 充电power度
        self.remain_mile = power * 3.0  # 每度电3英里


gcar = GasCar("BMW")
gcar.fill_fuel(50.0)
gcar.run(200.0)

ecar = EleCar('Tesla')
ecar.fill_fuel(60.0)
ecar.run(200.0)

结果:

BMW:run 200 miles!
Tesla:fuel out!

子类与父类

如果两个具有“一般-特殊”的逻辑关系,那么特殊类就可以作为一般类的“子类”来定义,从“父类”继承属性和方法

class <子类名>(<父类名>)def <重定义方法>(self,...):

覆盖
子类对象可以调用父类方法,除非这个方法在子类中重新定义了
如果子类同名方法覆盖了父类的方法,仍然还可以调用父类的方法

子类还可以添加父类中没有的方法和属性

# 加上排量
class GasCar(Car):
    def __init__(self, name, capacity):  # 名称和排量
        super().__init__(name)  # 父类初始化方法,只有名称
        # 调用父类的init生成一个汽车对象
        # 此时父类的__init__已被覆盖,使用super()返回父类
        self.capacity = capacity  # 增加了排量属性

关于self

在类定义中,所有方法的首个参数一般都是self

self的作用:在类内部,实例化过程中传入的所有数据都赋给这个变量

self实际上代表对象实例
<对象>.<方法>(<参数>)
等价于 ⇕ \quad \Updownarrow
<类>.<方法>(<对象>,<参数>)
这里的对象就是self

gcar = GasCar("BMW")
gcar.fill_fule(50.0)
gcar.run(200.0)
GasCar.run(gcar, 200.0)  # 等价于上一行

上机练习

创建一个类People:

包含属性:name, city
可以转换为字符串形式(__str__)
包含方法movato(self, newcity)
可以按照city排序
创建4个人对象,放到列表中进行排序

class People:
    def __init__(self, name, city):
        self.name, self.city = name, city

    def __str__(self):
        return "(%s, %s)" % (self.name, self.city)

    def moveto(self, new_city):
        self.city = new_city

    def __lt__(self, other):
        return self.city < other.city

    __repr__ = __str__  # 一定要加这一句,才能正常显示结果


s = list()
s.append(People("Jack", "qingdao"))
s.append(People("Amy", "jinan"))
s.append(People("Lillian", "shanghai"))
s.append(People("Nay", "beijing"))
print("Original:", s)
s.sort()
print("Sorted:", s)

结果:

Original: [(Jack, qingdao), (Amy, jinan), (Lillian, shanghai), (Nay, beijing)]
Sorted: [(Nay, beijing), (Amy, jinan), (Jack, qingdao), (Lillian, shanghai)]

在这有几个问题
问题1. 定义moveto()方法时,我写的是return People(self.name, new_city) ,
此时

a = People("Jack", "qingdao")
a.moveto("guangzhou")
print(a)

结果如下,并没有改变

Jack, qingdao

而将第二行再次赋值给a,就可得移动后的结果

a = People("Jack", "qingdao")
a = a.moveto("guangzhou")
print(a)

#  结果为:
Jack, guangzhou

而直接使用上边那个方法,则不用将值重新赋给a.
如果还是写a = a.moveto("guangzhou")的话,会提示:moveto没有返回任何值。

问题2:关于__repr____str__
区别可参见python中 str__和__repr. 这说的是在交互式命令行下的区别。
而我在实践过程中(用的是pycharm),若单独输出一个对象时print(a),不加__repr__ = __str__结果也正常显示。而创建多个对象,将其放在列表并排序,就必须要加__repr__ = __str__ . 还是不太理解原理

创建一个类Tercher

是People的子类,新增属性school
movate方法改为newschool
按照school排序
创建4个教师对象,放到列表中进行排序

在上面的基础上添加子类

class Teacher(People):
    def __init__(self, name, city, school):
        super().__init__(name, city)
        # self.name = name
        # self.city = city
        self.school = school

    def __str__(self):
        return "(%s, %s, %s)" % (self.name, self.city, self.school)

    __repr__ = __str__

    def moveto(self, new_school):
        self.school = new_school

    def __lt__(self, other):
        return self.school < other.school


s = list()
s.append(Teacher("Jack", "qingdao", "zhenhua"))
s.append(Teacher("Amy", "jinan", "chaoyang"))
s.append(Teacher("Lillian", "shanghai", "shuangyu"))
s.append(Teacher("Nay", "beijing", "guokai"))
print("Original:", s)
s.sort()
print("Sorted:", s)

结果为:

Original: [(Jack, qingdao, zhenhua), (Amy, jinan, chaoyang), (Lillian, shanghai, shuangyu), (Nay, beijing, guokai)]
Sorted: [(Amy, jinan, chaoyang), (Nay, beijing, guokai), (Lillian, shanghai, shuangyu), (Jack, qingdao, zhenhua)]

创建一个mylist类

创建一个Mylist类,继承自内置数据类型list(列表)
增加一个方法“累乘”product
def product(self):
返回所有数据项的乘积

class Mylist(list):
    def product(self):
        m = 1
        for i in self:
            m *= i
        return m


a = Mylist([1, 2, 3, 4])
print(a)
print(sum(a))
b = a.product()
print(b)
c = Mylist(range(1, 10))
print(c)
d = c.product()
print(d)

结果:

[1, 2, 3, 4]
10
24
[1, 2, 3, 4, 5, 6, 7, 8, 9]
362880

课后习题

选择题

class Person:
    def __init__(self, id):
        self.id = id


tom = Person(123)
tom.__dict__['age'] = 20  # __dict__可以除了类内定义的属性外,额外定义一个属性
print(tom.__dict__)
print(tom.age+len(tom.__dict__))  # len()为2

结果为:

{
    
    'id': 123, 'age': 20}
22

补充
字典类型:len(dict)计算字典元素个数,即键的总数。
Python中定义私有变量的方法是: __变量名.
假设a为类A的对象且包含一个私有数据成员“__value”,那么在类的外部通过对象a直接将其私有数据成员“__value”的值设置为3的语句可以写作 a._A__value=3 (不建议这样做)

学生成绩排序

设计一个学生类(Student),其中的数据成员有:字符串类型sname表示录入的学生姓名,整型值mscore代表学生的数学成绩,整型值cscore代表学生的语文成绩,整型值escore代表学生的英语成绩。

然后要求根据录入的学生成绩(各不相同),输出总分最高的学生姓名和各科目成绩。

输入
输入分为4个部分:
先输入学生姓名,以空格分隔;
然后输入学生的数学成绩(各不相同的正整数),也以空格分隔;
再输入学生的语文数学成绩(各不相同的正整数),也以空格分隔。
最后输入学生的英语数学成绩(各不相同的正整数),也以空格分隔。
学生姓名个数和成绩个数一定会相同。

jack tom
95 84
90 75
85 90

输出
共一行,为总分最高的学生姓名和各科目成绩,以空格分隔。

jack 95 90 85

class Student:
    def __init__(self, n, m, c, e):
        self.n, self.total = n, m+c+e
        self.m, self.c, self.e = m, c, e

    def __lt__(self, other):
        return self.total < other.total  # 判断条件为True时self排在前面,即较小的在前面

    def __str__(self):
        return '%s %d %d %d' % (self.n, self.m, self.c, self.e)

names = input().split()
mscore = list(map(int, input().split()))
cscore = list(map(int, input().split()))
escore = list(map(int, input().split()))
s = []
for i in range(len(names)):
    s.append(Student(names[i], mscore[i], cscore[i], escore[i])) 
# 此时s为 [jack 95 90 85, tom 84 75 90]
s.sort()  # 调用上边的特殊方法__lt__ 进行排序
print(s[-1])

两数求和问题

给定一个列表和一个整数目标,其中列表中的元素都为整数,要求返回列表中的两个元素的索引编号(以列表形式打印,为确保结果唯一,小的编号在前),使这两个元素的和为这个特定的目标。
(只对应确定的唯一一组解,并且不能使用同一个元素两次。)

输入:共两行,第一行为列表中的元素值(各不相同),以空格隔开,第二行为一个整数。

0 2 3
5

输出:共一行,为一个列表。
[1, 2]

def select(lst, s):
    for i in lst:
        for j in lst:
            if i != j and i+j == s:
                return sorted([lst.index(i), lst.index(j)])


lst = list(map(int, input().split()))
s = int(input())
print(select(lst, s))

"精致"的数

给定两个非负整数x和y,如果某一整数等于 x i + y j x^i+y^j xi+yj,其中整数 i>= 0 且 j>=0,那么我们认为该整数是一个"精致"的数。返回值小于或等于n(n<=200)的所有精致的数组成的列表。
结果列表中每个值最多出现一次,同时请使用sorted保证结果唯一。

输入:共三行,每一行为一个整数,分别是x y n

1
2
5

输出:共一行,为一个列表。

[2, 3, 5]

def select(x, y, n):
    r = set()  # 集合可以去重
    for i in range(n):
        for j in range(n):
            s = x**i + y**j
            if s <= n:
                r.add(s)
    return sorted(r)  # 输入值可以为任何容器,返回值为列表


x = int(input())
y = int(input())
n = int(input())
print(select(x, y, n))

最大的周长

给定一个列表alist,alist由一些正整数(代表长度)组成,返回由alist中的三个长度组成的有效三角形的最大周长。如果所有的长度组合都不能构成有效三角形,则返回 0。

输入:共一行,列表中的元素以空格隔开。

2 1 2

输出:共一行,为一个非负整数,是最大三角形的周长

5

a_list = list(map(int, input().split()))
a_list.sort()  # 从小到大排序
while len(a_list) >= 3:
    a = a_list.pop()  # 取出最大值
    if a < a_list[-1] + a_list[-2]:  # 看跟次小的两个可否组成三角形
        print(a + a_list[-1] + a_list[-2])
        break
else:  # 全都无法构成三角形
    print(0)

.pop() 函数用于移除列表中的一个元素(默认最后一个元素),更新列表并且返回移除元素的值。

猜你喜欢

转载自blog.csdn.net/weixin_43429677/article/details/108195332
今日推荐