Python学习笔记¶

第十章 Python面向对象¶

10.1 什么是对象?¶

  • 对象 = 属性 + 方法
  • Python中,万物都可以是对象
In [4]:
print(type(1))
print(id(1))
print(dir(1)) # 查看属性和方法
<class 'int'>
4338286832
['__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__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
In [5]:
print(type("a"))
print(id("a"))
print(dir("a"))
<class 'str'>
4339687920
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
In [6]:
print(abs(-1))
print(id(abs))
print(type(abs))
print(dir(abs))
1
4338894384
<class 'builtin_function_or_method'>
['__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__']
  • 对象是类的实例,是程序的基本单元
  • 有对象需要先定义一个类
  • 同一个类有相同的属性和方法,但id不同
  • 可以用多个名称引用同一个对象,但只有一个id
In [7]:
a = complex(2,5)
b = a
print(id(a))
print(id(b))
4709526608
4709526608
  • 对象实现了属性和方法的封装
  • 提高了灵活性、重用性、扩展性
  • 引用形式:对象.属性名
In [11]:
print("abc".upper())
print((1+2j).real)
print((1+2j).imag)
ABC
1.0
2.0
  • Python是动态的语言,使得对象可以随时增加或者删除属性或者方法
  • 用del就能删掉对应的属性
  • 直接赋值就能增加对应的属性

  • OOP:面向对象编程

10.2 类的定义和调用¶

  • 什么是类:对象的模板,class
  • 实例对象是类的具体化
  • 封装性、继承性、多态性
  • 和函数相似,是一系列代码的封装
  • 类名大写字母开头,函数小写字母开头
  • 类名使用驼峰命名法,CamelCase

  • 自定义类:class语句

class ClassName:
    def __init__(self,*param): # 构造函数
        print("哈喽啊")
    def another_method(self,*param):
        print("你好呀")
  • __init__(self,参数表)是一个特殊的函数名,是初始化函数
  • 一般来讲第一个参数必须为self
  • 函数里第一个参数是self,则对自己进行操作
  • 按照惯例写成self,但是要真的写成别的也没事
  • 调用的时候,类名(参数)
    • 如果调用类,会直接创建一个对象
    • 所以一般obj = 类名(参数)
    • 返回一个对象实例
    • 类方法中的self就是指向自己这个实例的
  • 使用.来进行调用

例:创建一个力对象

In [17]:
class Force:
    def __init__(self,x,y):
        self.fx , self.fy = x,y

    def show(self):
        print("fx = {} , fy = {}".format(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)
f2.show()
f_new = f1.add(f2)
f_new.show()
fx = 0 , fy = 1
fx = 3 , fy = 4
fx = 3 , fy = 5

由上面,发现,不用全列出来,随时出现变量名,随时用。

这就是动态特性。

10.3 类定义中的特殊方法¶

  • 特殊方法Special method:
    • 也被称作魔术方法,magic method
    • 在类定义中实现一些特殊方法,可以方便实用python一些内置操作
    • 所有特殊方法都是用双下划线开始和结束
    • 比如__init__
  • 构造函数 __init(self,*param)__
  • 析构函数 __del(self,*param)__
In [19]:
import os.path
class FileManager:
    def __init__(self,filepath,filename):
        self.file = open(os.path.join(filepath,filename))
        print("文件打开成功")

    def __del__(self):
        self.file.close()
        del self.file
        print("文件已关闭")

f = FileManager("/Users/caiguu/PycharmProjects/pythonLearn","新时代.txt")
del f
文件打开成功
文件已关闭
  • __add__(self,other):实现了就能重载+运算
  • 包括add、sub、mul、div分别可以实现+ - * /
  • 反运算:左操作数不支持的时候调用 __radd__(self,other) 还有rsub rmul rdiv
  • 大小比较:eq ne lt gt le ge 都是__开头和结尾的,返回True或者False
  • __str__相当于Java里面的toString
In [23]:
class Force:

    def __init__(self,x,y):
        self.fx , self.fy = x,y

    def show(self):
        return "fx = {} , fy = {}".format(self.fx,self.fy)

    def add(self,force2):
        # 不改自己,生成一个新的对象
        x = self.fx+force2.fx
        y = self.fy+force2.fy
        return Force(x,y)

    __add__ = add
    __str__ = show

    def __mul__(self, n): # f*3.5这种形式
        x,y = self.fx*n,self.fy*n
        return Force(x,y)

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

    def __rmul__(self, n): # 3.5*f这种形式
        x,y = self.fx*n,self.fy*n
        return Force(x,y)
    # 也可以直接__rmul__ = __mul__

f1 = Force(1,4)
f2 = Force(5,1)
print(f1+f2)
print(f1==f2)
print(3.5*f1)
print(f2*1.14)
f3 = f1 + f2
print(f3)
fx = 6 , fy = 5
False
fx = 3.5 , fy = 14.0
fx = 5.699999999999999 , fy = 1.14
fx = 6 , fy = 5
  • 字符串操作
    • 不仅可以是数学类型,也可以是字符串类型
    • str(self) 相当于toString,返回一个字符串
    • repr(self) 比较正式的str函数,返回一个字符串
    • len(self) 对于容器操作,返回元素个数
    • 都需要双下划线开始和结束
  • 除此之外,还有常用的诸如:
    • setitem 按照索引赋值
    • getitem 按照索引获取值
    • cmp 比较运算
    • call 函数调用
    • mod 求余数
    • pow 乘方

10.4 自定义对象的排序¶

  • 列表排序:ls.sort()
    • 默认小的在前面,否则reverse=True
    • 如果都是字符串,默认字母表顺序升序排列
  • 通用函数sorted()
    • 类似sort,但是返回列表的副本,原列表不变
  • 只有当列表中的元素全是同一种类型时,它们才会正常工作
In [24]:
name = ["John","Bob","Alice","olg","肖+号"]
name.sort()
print(name)
name = ["John","Bob","Alice","olg","肖+号"]
name.sort(reverse=True)
print(name)
name = ["John","Bob","Alice","olg","肖+号"]
name_new = sorted(name)
print(name)
print(name_new)
['Alice', 'Bob', 'John', 'olg', '肖+号']
['肖+号', 'olg', 'John', 'Bob', 'Alice']
['John', 'Bob', 'Alice', 'olg', '肖+号']
['Alice', 'Bob', 'John', 'olg', '肖+号']
  • 如果自己重写__lt__,name就可以自定义排序方法
  • def __lt__(self,y)
  • y通常是同类的另一个实例
  • 返回True,表示确实小,排在前面
In [30]:
class Force:

    def __init__(self,x,y):
        self.fx , self.fy = x,y

    def show(self):
        return "fx = {} , fy = {}".format(self.fx,self.fy)

    def add(self,force2):
        # 不改自己,生成一个新的对象
        x = self.fx+force2.fx
        y = self.fy+force2.fy
        return Force(x,y)

    __add__ = add
    __str__ = show
    __repr__= show

    def __mul__(self, n): # f*3.5这种形式
        x,y = self.fx*n,self.fy*n
        return Force(x,y)

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

    def __rmul__(self, n): # 3.5*f这种形式
        x,y = self.fx*n,self.fy*n
        return Force(x,y)
    # 也可以直接__rmul__ = __mul__

    def __lt__(self, y):
        value1 = self.fx**2+self.fy**2
        value2 = y.fx**2+y.fy**2
        return value1<value2

f1 = Force(1,4)
f2 = Force(5,5)
print(f1<f2)
forces = [Force(1,4),Force(2,3),Force(6,6),Force(1,1)]
forces.sort()
print(forces)
for f in forces:
    print(f)
True
[fx = 1 , fy = 1, fx = 2 , fy = 3, fx = 1 , fy = 4, fx = 6 , fy = 6]
fx = 1 , fy = 1
fx = 2 , fy = 3
fx = 1 , fy = 4
fx = 6 , fy = 6

学生类,成绩由高到低排序:

In [35]:
class Student:
    def __init__(self,name,grade):
        self.name = name
        self.grade = grade

    def __lt__(self, other):
        return self.grade>other.grade

    def __str__(self):
        return "学生%s,成绩%d" % (self.name,self.grade)

    __repr__=__str__

s = list()
s.append(Student("Jack",100))
s.append(Student("Alice",99))
s.append(Student("Bob",59))
s.append(Student("olg",63))
s.append(Student("x+h",95))
s.append(Student("Niconiconi",93))
print(s)
s.sort()
print(s)
[学生Jack,成绩100, 学生Alice,成绩99, 学生Bob,成绩59, 学生olg,成绩63, 学生x+h,成绩95, 学生Niconiconi,成绩93]
[学生Jack,成绩100, 学生Alice,成绩99, 学生x+h,成绩95, 学生Niconiconi,成绩93, 学生olg,成绩63, 学生Bob,成绩59]

根据调试得出结论,无论是否reverse,只调用了lt方法。

当然还可以定义其他的比较符,重载相关的符号运算。

10.5 类的继承¶

  • 类的继承机制
  • 继承:inheritance
  • 代码复用:已有的类能够衍生出新的类

例:车辆续航里程

In [38]:
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):
        if self.remain_mile >= miles:
            self.remain_mile -= miles
            print("%s: 车辆跑了 %d 公里!" % (self.name,miles))
        else:
            print("%s: 燃料耗尽!" % self.name)

class GasCar(Car):
    def fill_fuel(self,gas):
        self.remain_mile += gas * 6

class ElecCar(Car):
    def fill_fuel(self,power):
        self.remain_mile += power * 3

gcar = GasCar("BMW")
gcar.fill_fuel(50)
gcar.run(200)

ecar = ElecCar("Tesla")
ecar.fill_fuel(60)
ecar.run(200)
BMW: 车辆跑了 200 公里!
Tesla: 燃料耗尽!
  • 一般和特殊的关系,可以表现为父类-子类之间的关系
  • 语法:class 子类名(父类名)
  • Override 重写、覆盖:子类可以调用父类的方法,除非这个方法在子类中被定义了
  • 就算子类覆盖了父类的方法,也可以调用到父类的那个方法
  • 子类还可以添加父类中没有的方法和属性
  • 需要使用super调用父类的各种方法,比如构造。看下面的例子:
In [39]:
class GasCarNew(Car):
    def __init__(self,name,capacity):
        super().__init__(name)
        self.capacity = capacity
  • 关于self
  • 在类定义中,所有方法的第一个参数都是self
  • 可以改成别的名,但是容易出奇奇怪怪的问题,而且不符合约定
  • 作用:在类内部,实例化过程中传递的所有参数都会给self
  • 也就是说对象把自己传给self,self指向那个对象
  • 对象.方法(参数) 等价于 类.方法(对象,参数)
  • 如下例所示:
In [41]:
gcar_new = GasCarNew("BMW",114514)
gcar_new.fill_fuel(20)
GasCar.fill_fuel(gcar_new,50)
gcar_new.run(30)
GasCar.run(gcar_new,50)
BMW: 车辆跑了 30 公里!
BMW: 车辆跑了 50 公里!

也就是说,对象.方法(其他参数),等价于类.方法(类变量,其他参数)

但是一般不按后者那么去写,太累赘了!

10.6 练习题三道¶

10.6.1 People类¶

In [45]:
class People:
    def __init__(self,name,city):
        self.name = name
        self.city = city
    def moveto(self,newcity):
        self.city = newcity
    def __str__(self):
        return "<name = %s , city = %s>" % (self.name,self.city)
    def __lt__(self, other):
        return self.name < other.name
    __repr__ = __str__

p1 = People("张三","河北承德")
p2 = People("肖加号","福建莆田")
p3 = People("奥利给","上海浦东")
p4 = People("olg","北京海淀")
ls = [p1,p2,p3,p4]
print(ls)
ls.sort()
print(ls)
p3.moveto("北京东城")
print(p3)
[<name = 张三 , city = 河北承德>, <name = 肖加号 , city = 福建莆田>, <name = 奥利给 , city = 上海浦东>, <name = olg , city = 北京海淀>]
[<name = olg , city = 北京海淀>, <name = 奥利给 , city = 上海浦东>, <name = 张三 , city = 河北承德>, <name = 肖加号 , city = 福建莆田>]
<name = 奥利给 , city = 北京东城>

10.2 子类和父类¶

In [47]:
class Teacher(People):
    def __init__(self,name,city,school):
        self.name = name
        self.city = city
        self.school = school
    def moveto(self,newschool):
        self.school = newschool
    def __lt__(self, other):
        return self.school < other.school
    def __str__(self):
        return "<name = %s , city = %s , school = %s>" % (self.name,self.city,self.school)
    __repr__ = __str__

t1 = Teacher("zzz","河北承德","BJFU")
t2 = Teacher("xjh","福建莆田","BUPT")
t3 = Teacher("jxh","福建莆田","THU")
t4 = Teacher("hjx","福建莆田","PKU")
ls = [t1,t2,t3,t4]
print(ls)
ls.sort()
print(ls)
t2.moveto("加里敦大学")
print(ls)
[<name = zzz , city = 河北承德 , school = BJFU>, <name = xjh , city = 福建莆田 , school = BUPT>, <name = jxh , city = 福建莆田 , school = THU>, <name = hjx , city = 福建莆田 , school = PKU>]
[<name = zzz , city = 河北承德 , school = BJFU>, <name = xjh , city = 福建莆田 , school = BUPT>, <name = hjx , city = 福建莆田 , school = PKU>, <name = jxh , city = 福建莆田 , school = THU>]
[<name = zzz , city = 河北承德 , school = BJFU>, <name = xjh , city = 福建莆田 , school = 加里敦大学>, <name = hjx , city = 福建莆田 , school = PKU>, <name = jxh , city = 福建莆田 , school = THU>]

10.3 mylist类创建¶

这是对内置类型的继承!

In [59]:
class MyList(list):

    def product(self):
        p = 1
        for i in range(len(self)):
            p *= self[i]
        return p
    # 也可以 for i in self:; p*=i

ls = MyList([1,1,4,5,1,4,1,9,1,9,8,1])
print(ls)
print(ls.product())
print(MyList.product(ls))
[1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 1]
51840
51840