本系列是学习 廖雪峰 Python3 教程 过程中记录的笔记,本篇文章记录 Python
中面向对象的高级编程,主要内容有 __slots__
(限制属性)、@property
(简化代码)、多重继承、定制类里面的一些小组件、枚举类和元类。
面向对象 高级编程
使用 __slots__
-
用途:限制实例的属性,可以控制有哪些属性;
-
可以给实例绑定任何属性和方法,但是仅对本实例有效;
-
可以给
class
绑定任何属性和方法,对该类的所有实例都有效; -
使用
__slots__
可以限制实例的属性,用tuple
定义允许绑定的属性名称,这种方法仅对本类的实例起作用,对继承的子类的实例不起作用;class Student(object): pass def set_score(self, score): self.score = score from types import MethodType Student.set_score = MethodType(set_score, Student) # 绑定类方法 Student.school = "uestc" # 绑定类属性 s = Student() s.set_score(score=30) # 实例1修改类属性的值 print(s.score, s.school) s_2 = Student() # 该类的第二个实例 print(s_2.score, s_2.school) s_2.set_score(score=50) # 实例2修改类属性的值 print(s.score) class Student_s(object): __slots__ = ('school', 'age') # 用tuple定义允许绑定的属性名称 class GraduateStudent(Student_s): __slots__ = ('score') pass s_s = Student_s() # s_s.score = 90 # 报错,该类允许的属性中没有 score g_s = GraduateStudent() g_s.score = 100 # 可运行,子类属性中添加了 score print(g_s.score) # g_s.name = "morton" # 报错,该类及其父类允许的属性中没有 name output—————————————————— 30 uestc 30 uestc 50 100
使用 @property
-
用途:
@property
广泛应用在类的定义中,简化代码,同时保证对参数进行必要的检查; -
使用
@mothod.setter
时,同名的mothod
一定要有@property
修饰; -
【注】一下代码中的
_xxx
表示这是私有变量,去掉下划线,在赋值的时候会出现递归溢出;# 请利用@property给一个Screen对象加上 width # 和 height属性,以及一个只读属性resolution: class Screen(object): @property # width 可读 def width(self): return self._width @property # height 可读 def height(self): return self._height @property # resolution 可读 def resolution(self): return self._width * self._height @width.setter # width 可写 def width(self, value): print('width 此处进行参数检查······') self._width = value @height.setter # height 可写 def height(self, value): print('height 此处进行参数检查······') self._height = value # 测试: scr = Screen() scr.width = 1024 scr.height = 768 print('width =', scr.width, 'height =', scr.height, 'resolution =', scr.resolution) if scr.resolution == 786432: print('测试通过!') else: print('测试失败!') output———————————————— width 此处进行参数检查······ height 此处进行参数检查······ width = 1024 height = 768 resolution = 786432 测试通过!
多重继承
- 用途:通过多重继承,子类可以同时获得多个父类的所有功能;
MixIn
设计的目的是给一个类增加多个功能,与组合类似,优先考虑通过多重继承来组合实现多个MixIn
的功能,而非设计层级复杂的继承关系;Python
允许多重继承,Java
只允许单一继承;
定制类
-
用途:为类添加一些小功能,类似于自定义打印对象的输出、使对象具有函数的调用性质等;
-
仔细查看以下示例和解析,涉及到的定制类都有具体用法:
class Chain(object): count = 0 def __init__(self, path=''): # 初始化,一旦定义新的实例就会执行 self.__path = path Chain.count += 1 # 记录初始化的次数 print("count = ", Chain.count) def __getattr__(self, path): # 动态返回一个属性,需要的属性未定义时自动调用 print("path : ", path) return Chain('%s/%s' % (self.__path, path)) def __call__(self, name): # 使对象Callable,可以像普通函数一样调用 print("name : ", name) return Chain('%s/%s' % (self.__path, name)) def __str__(self): # 自定义打印对象时的输出 print("这里可以自定义,输出你喜欢的东西") return self.__path __repr__ = __str__ # 自定义打印对象时的输出,为调试服务 print(Chain().users('michael').repos) output———————————————— count = 1 path : users count = 2 name : michael count = 3 path : repos count = 4 这里可以自定义,输出你喜欢的东西 /users/michael/repos
-
【解析】
Step 1: Chain() # 实例化 Step 2: Chain().users # 由于没有给实例传入初始化对应的users属性的具体信息,从而自动调用__getattr__()函数,从而有: Chain().users = Chain('\users') # 这是重建实例,新实例的属性 new.__path = \users; Step 3: Chain().users('michael') # 由于定义了__call__()函数,该实例可以直接像普通函数一样调用,michael 是函数参数,从而有: Chain().users('michael') = Chain('\users')('michael') = Chain('\users\michael') # 【关键】再一次重建实例,覆盖掉 Chain('\users'),新实例的属性 renew.__path = \users\michael; Step 4: Chain().users('michael').repos # 这一步是查询renew实例的属性repos,由于没有这一属性,就会执行__getattr__()函数,再一次返回新的实例Chain('\users\michael\repos')并且覆盖点之前的实例, # 这里记 trinew =Chain('\users\michael\repos'),不要忘了,一旦定义了一个新的实例,就会执行__init__方法; Step 5: print(Chain().users('michael').repos) = print(trinew) # 由于定义了__str__() 方法,那么打印的时候就会调用此方法,据此方法的定义,打印回来的是trinew的__path属性,即 \users\michael\repos # 至此,我们也把所有定义的有特殊用的方法都用上了,完毕。
枚举类
-
用途:固定的可以列举的对象;
-
from enum import Enum, unique
-
Enum
: 通过继承Enum
类可以实现枚举类 ;unique
: 避免出现重复的枚举类型(value相同); -
若没有使用装饰器
@unique
,而枚举类中又出现 value相同的枚举类型,在遍历的时候后面的成员会被忽略,而要能遍历到重复的成员,需要使用一个特殊的属性__members__
,它包含了枚举类中的所有成员。from enum import Enum, unique Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) @unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value) for name, member in Weekday.__members__.items(): print(name, '=>', member, ',', member.value) print("访问 Mon 的几种方法:") print("1. ", Weekday.Mon) print("2. ", Weekday['Mon']) print("3. ", Weekday(1)) output—————————————— Jan => Month.Jan , 1 Feb => Month.Feb , 2 Mar => Month.Mar , 3 Apr => Month.Apr , 4 May => Month.May , 5 Jun => Month.Jun , 6 Jul => Month.Jul , 7 Aug => Month.Aug , 8 Sep => Month.Sep , 9 Oct => Month.Oct , 10 Nov => Month.Nov , 11 Dec => Month.Dec , 12 Sun => Weekday.Sun , 0 Mon => Weekday.Mon , 1 Tue => Weekday.Tue , 2 Wed => Weekday.Wed , 3 Thu => Weekday.Thu , 4 Fri => Weekday.Fri , 5 Sat => Weekday.Sat , 6 访问 Mon 的几种方法: 1. Weekday.Mon 2. Weekday.Mon 3. Weekday.Mon
使用元类(metaclass
)
-
用途:
type()
可以动态创建类,metaclass
可以控制类的创建行为,例如:动态增加类的方法(使用需谨慎,一般也用不到); -
type()
函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()
函数创建出Hello
类,而无需通过class Hello(object)...
的定义: -
要创建一个class对象,
type()
函数依次传入3个参数:class
的名称;- 继承的父类集合,注意
Python
支持多重继承,如果只有一个父类,别忘了tuple
的单元素写法; class
的方法名称与函数绑定,这里我们把函数fn
绑定到方法名hello
上。
def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class h = Hello() h.hello() output———————————— Hello, world.