第五章 面向对象编程 v1.0
第一部分 面向对象基础
一、类与对象
1.概念
①类与对象
- 类: 对具有相同属性和方法的一组对象的描述或定义
- 对象: 具体对应的某项事物
②构造方法
构造方法(__init__
)是一个特殊的实例方法,它在创建类的新实例时自动调用。
当创建一个对象时,会自动调用该类的__init__
方法,用于初始化新创建的对象的状态。
③实例方法
实例方法是定义在类中的函数,用于操作类的实例,它至少有一个参数,通常命名为self
。 self
引用调用该方法的对象本身。通过self
,实例方法可以访问和修改对象的属性。
2.定义类
class 类的名称:
"""类的说明文档"""
def __init__(self, [参数列表]): # 构造方法,该方法在类实例化时会自动调用
pass # 构造方法主体
def func(self, [参数列表]): # 实例方法
pass
pass # 定义其他方法
类名([实参列表]) # 参数同构造方法,实际上调用了__init__()构造方法进行类的初始化
# 类的成员方法与普通的函数有一个明显的区别——它们有一个额外的第一个参数名称, 一般为self
class 类的名称:
def func(self, [参数列表]): # 类方法中,self代表类的实例
pass
class Person:
def __init__(self,name):
self.name=name
def get_name(self):
return self.name
def set_name(self,name):
self.name=name
me=Person("hymsk")
print(me.get_name()) # output: hymsk
3.魔术方法
魔术方法是类定义中在名称的开始和结尾都有双下划线的一类特殊方法
魔术方法被用来执行一些普通方法无法执行的功能,__init__
是我们遇到的最常用的魔术方法
魔术方法的定义与之前的构造方法定义一致,示例如下:
class 类的名称:
def __魔术方法__(self, [参数列表]): # 不同魔术方法有不同的参数
pass # 魔术方法主体
魔术方法在Python中有很多作用,最常见的即是运算符重载,具体介绍如下:
①运算符重载
在Python中,可以通过魔术方法来实现运算符的重载
class Score:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Score(self.value + other.value)
def get_value(self):
return self.value
a = Score(18)
b = Score(20)
print((a + b).get_value()) # output: 38
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
a = Person("a", 18)
b = Person("b", 20)
print(a == b) # output: False
运算符与魔术方法对应关系
算数运算符:
魔术方法 | 对应运算符 | 魔术方法 | 对应运算符 |
---|---|---|---|
__sub__ | - | __pow__ | ** |
__mul__ | * | __and__ | & |
__truediv__ | / | __xor__ | ^ |
__floordiv__ | // | __or__ | | |
比较运算符:
魔术方法 | 对应运算符 | 魔术方法 | 对应运算符 |
---|---|---|---|
__lt__ | < | __ne__ | != |
__le__ | <= | __gt__ | > |
__eq__ | == | __ge__ | >= |
②其他作用
其他魔术方法及作用
魔术方法名 | 作用 |
---|---|
__len__ | 获取该对象长度 |
__getitem__ | 获得对象对应索引 |
__setitem__ | 分配索引值 |
__delitem__ | 删除索引值 |
__iter__ | 设置迭代对象(用于for循环等) |
__contains__ | 对应in |
__call__ | 设置作为函数调用对象时的值 |
__int__, __str__, ... | 设置将对象转化为内建对象得到的结果 |
4.实例变量与类变量
- 实例变量:定义在类的实例对象上的变量。每个实例都有自己的实例变量副本,这些变量与特定的对象实例相关联。
- 类变量:定义在类本身上而不是其实例上的变量。类变量属于类本身,在所有实例之间是共享的,所有实例都可以访问和修改类变量。
class Utils:
name="utils"
@classmethod
def get_name(cls):
return cls.name
u1 = Utils()
u2 = Utils()
print(Utils.name, u1.name, u2.name) # output: utils utils utils
u1.name = "123"
print(Utils.name, u1.name, u2.name) # output: utils 123 utils => 修改了实例变量
Utils.name = "321"
print(Utils.name, u1.name, u2.name) # output: 321 123 321 => 当实例变量与类变量同名时,优先获得实例变量
二、继承
继承是指在原有类的基础上,进行功能扩展,创建新的类型。
在Python中,继承既支持单继承也支持多继承。
1.单继承
单继承一个类只继承自一个父类,结构简单,易于理解和维护。
class 子类名(父类名):
def __init__(self, [参数列表]):
super().__init__([参数列表]) # 调用父类的构造方法,需要写在子类构造函数第一行
# ...
class Person:
def __init__(self,name):
self.name=name
def get_name(self):
return self.name
def set_name(self,name):
self.name=name
class Teacher(Person):
def __init__(self, name,id):
super().__init__(name)
self.id=id
2.多继承
多继承允许一个类同时继承自多个父类,具有更大的灵活性,但也增加了代码的复杂性。
class 子类名(父类1, 父类2, ...):
def __init__(self, [参数列表]):
# super().__init__([参数列表]) # super()默认调用第一个父类的构造方法
父类1.__init__(self, [参数列表])
父类2.__init__(self, [参数列表])
# ...
class Person:
def __init__(self,name):
self.name=name
def get_name(self):
return self.name
def set_name(self,name):
self.name=name
class Staff:
def __init__(self,id):
self.id=id
def get_id(self):
return self.id
def set_id(self,id):
self.id=id
class Teacher(Person,Staff):
def __init__(self, name, id):
Person.__init__(self,name)
Staff.__init__(self,id)
第二部分 函数式编程
在Python中,函数名可以被赋值给一个变量,也可以作为参数使用进行函数式编程
一、装饰器
用于扩展其他不想修改的函数的功能的函数,装饰器有助于让我们的代码更简短。
def symbol(func):
def wrap():
# 装饰器逻辑
func() # 被装饰函数调用
# 装饰器逻辑
return wrap
@symbol # 使用
def function():
pass
def decor(func): #装饰器函数
def wrap():
print("===", end="")
func()
print("===", end="")
return wrap
@decor #预定义函数定义 -> print_text=decor(print_text)
def print_text():
print("Hello world!", end="")
print_text() # output: ===Hello world!===
二、迭代器与生成器
之前有讲过可迭代对象,如list等,迭代器和生成器都是一类特殊的可迭代对象
①可迭代对象(Iterable)
之前讲到可迭代对象还不是特别详细,可迭代对象语法上具体应当指实现了__iter__()
方法;在语义上还应当直接或间接(通过__iter__()
方法返回可迭代对象)实现__next__()
方法,如此才能被正常迭代,直接自身实现了__next__()
方法的又叫迭代器。
语法上可用通过:isinstance(对象名, Iterable)
来判断对象是否为可迭代对象
class MyRange():
def __iter__(self):
return None
r=MyRange()
print(isinstance(r, Iterable)) # output: True
[i for i in r] # TypeError: iter() returned non-iterator of type 'NoneType' => 不能被for循环遍历
class MyRange():
def __iter__(self):
return enumerate(range(2))
r=MyRange()
print(isinstance(r, Iterable)) # output: True => 是可迭代对象
print(isinstance(r, Iterator)) # output: False => 不是迭代器
print([i for i in r]) # [(0, 0), (1, 1)]
class MyRange():
def __init__(self):
self.number=0
def __iter__(self):
return self
def __next__(self):
self.number+=1
if(self.number<5):
return self.number
else:
raise StopIteration
r=MyRange()
print(isinstance(r, Iterable)) # output: True => 是可迭代对象
print(isinstance(r, Iterator)) # output: True => 是迭代器
print([i for i in r]) # [1, 2, 3, 4]
②迭代器(Iterator)
类似上面的直接实现__next__()的类,实现了
__next__
方法和__iter__
方法的类是迭代器类
之前学习到的enumerate()
返回的结果为迭代器,而range()
返回的是可迭代对象
可迭代对象和迭代器的区别就在于next()方法,迭代器可以通过next()方法不断的获取下一个值,直到所有元素全部输出完之后,返回StopIteration异常才会停止。
e = enumerate(range(2))
print(e.__next__()) # output: (0, 0)
print(e.__next__()) # output: (1, 1)
print(e.__next__()) # StopIteration
from typing import Iterable, Iterator
r = range(10)
e = enumerate(range(10))
print(isinstance(r, Iterable), isinstance(r, Iterator)) # output: True False
print(isinstance(e, Iterable), isinstance(e, Iterator)) # output: True True
③生成器(Generator)
生成器是一类特殊的迭代器,生成器不允许使用任意索引,但是可以通过for循环迭代
使用生成器可以提高性能,在开始使用之前,不会先生成所有元素,这意味着会有更低的内存使用率
一般使用函数与yield语句创建生成器,在for循环中直接作为可迭代对象即可
def countdown():
i=5
while i > 0:
yield i
i -= 1
c = countdown()
print(isinstance(c, Iterable)) # output: True
print(isinstance(c, Iterator)) # output: True
print(isinstance(c, Generator)) # output: True
for i in countdown(): # output: 5 4 3 2 1
print(i,end=' ')
三、itertools模块
一个用于生成迭代器与操作迭代器的标准库中的模块
1.生成迭代器
# 返回一个从n无限增加的迭代器,默认步长为1
itertools.count(n[, step])
# 返回一个将某可迭代对象中各元素无限次迭代的迭代器
itertools.cycle(iterable)
# 返回一个将某对象重复无限(或指定n次)的迭代器
itertools.repeat(obj[, n])
2.映射与过滤功能
# 返回从第一次判定函数func返回False时截断的新迭代器
itertools.takewhile(func, iterable)
# 组合几个可迭代对象结合成一个迭代器并返回
itertools.chain(iterable1, iterable2, ...)
# 返回一个迭代器,各结果为前一次迭代结果与当前值运算结果(默认求和)
itertools.accumulate(iterable[, func])
print(list(itertools.takewhile(lambda x:x<5, range(10)))) # output: [0, 1, 2, 3, 4]
print(list(itertools.chain(range(0,2), range(5,8)))) # output: [0, 1, 5, 6, 7]
print(list(itertools.accumulate(range(10)))) # output: [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
print(list(itertools.accumulate(range(10), lambda x,y:x+y))) # output: [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
print(list(itertools.accumulate(range(10), lambda x,y:x+2*y))) # output: [0, 2, 6, 12, 20, 30, 42, 56, 72, 90]
3.组合功能
# 按顺序对几个迭代器分别进行组合(笛卡尔积),返回结果为结果的元组的迭代器
itertools.product(可迭代对象列表)
# 返回该迭代器的排列结果元组(n默认为迭代器中元素个数,即全排列)的迭代器
itertools.permutations(可迭代对象[,n])
print(list(itertools.product(range(3),range(3))))
# output: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
print(list(itertools.permutations(range(3))))
# output: [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
四、高阶函数
一个函数以另一个作为参数,或返回值为一个函数时称为高阶函数,常用高级函数有如下
# 映射函数:根据提供的函数对指定序列做映射,返回值为新的迭代器
map(func, iterable)
# 过滤函数:根据提供的布尔函数对指定序列中各值做判断,删除不匹配项返回新迭代器
filter(func, iterable)
# 排序函数:对一个集合按照指定规则(key)进行排序(默认升序排列),返回一个新的排序后的集合
sorted(iterable, key = None, reverse = False)
print(list(map(lambda x:x**2, range(4)))) # 映射函数
# output: [0, 1, 4, 9]
print(list(filter(lambda x:x%2==0, range(4)))) # 过滤函数
# output: [0, 2]
print(list(sorted(range(4), key=lambda x:(x-2)**2))) # 排序函数
# output: [2, 1, 3, 0] => 根据规则计算结果为[0, 1, 1, 4]
第三部分 面向对象进阶
一、类方法(@classmethod)
通常对象的方法被一个类的实例调用,类方法不同,它被一个类所调用
类方法传递参数cls而非self,类方法中的参数cls指向本类对象本身,
class 类名:
@classmethod
def 类方法名(cls): # cls代表类
pass
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
@classmethod
def new_square(cls, side_length):
return cls(side_length, side_length)
square = Rectangle.new_square(5) # 可以直接调用
print(square.calculate_area()) # output: 25
二、静态方法(@staticmethod)
与类方法类似,但静态方法则不会自动绑定第一个参数到类本身,且行为与纯函数类似
class 类名:
@staticmethod
def 静态方法名():
pass
class Utils:
name="utils"
@classmethod
def get_name(cls):
return cls.name
@staticmethod
def get_name_(): # 实际上与上面的get_name()方法行为一致
return Utils.name
三、封装
封装是面向对象编程中的核心概念之一,指将数据和方法以一定的规则组织在类中,限制类外部的访问,从而实现类内部数据的保护和安全。
1.弱私有方法与属性
在名称的开头添加一个下划线,这表示它们是私有的,不应该被外部代码使用(只是约定)
2.强私有方法与属性
使用强私有方法与属性实际上也不能确保他们保持私有
使用双下划线前缀定义的名称会被Python解释器重命名,使其在外部无法被访问
实际上,Python会在标识符前添加一个下划线和类名,这样可以避免在继承中产生名称冲突
class Spam:
def __init__(self):
self.__egg = 7
def print_egg(self):
print(self.__egg)
s=Spam()
s.print_egg() # output: 7
print(s._Spam__egg) # output: 7 => 可以通过 object._className__name 访问
print(s.__egg) # AttributeError
3.操作私有属性
通过
@property
、@xxx.setter
和@xxx.deleter
装饰器分别可以访问、修改和删除私有属性
此时并未学习装饰器,可以将其当暂时先做一种语法,后期学习完装饰器再重新温习
class Utils:
def __init__(self,name):
self.__name=name # 定义了私有 __name 属性
@property
def name(self): # 通过 obj.name 即可访问私有 __name 属性
return self.__name
@name.setter
def name(self, value): # 通过 obj.name = value 即可修改私有 __name 属性
self.__name=value
@name.deleter
def name(self): # 通过 del obj.name 即可删除私有 __name 属性
self.__name=None
四、抽象
Python中通过
abc
模块的ABC
类和@abstractmethod
装饰器来定义抽象基类和抽象方法
from abc import ABC, abstractmethod
1.抽象类
Python中定义一个抽象类需要继承ABC类
class 抽象类名(ABC):
pass
2.抽象方法
抽象方法规定了子类必须实现的方法,使用
@abstractmethod
装饰器进行修饰
class 抽象类名(ABC):
@abstractmethod
def 抽象方法名(self):
pass
from abc import ABC,abstractmethod
import math
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self,r):
self.r = r
def area(self):
return math.pi*(self.r**2)
class Square(Shape):
def __init__(self,a):
self.a = a
def area(self):
return self.a**2
print(Circle(2).area()) # output: 12.566370614359172
print(Square(2).area()) # output: 4
Shape() # TypeError: Can't instantiate abstract class Shape with abstract method area