'限制实例的属性:
__slots__
,把一个方法变成属性调用:@property
装饰器,多重继承&MixIn
,定制类,枚举类:Enum,元类'
限制实例的属性:__slots__
限制实例的属性 比如只允许对Student实例添加name和age属性。 由于'score'没有被放到
__slots__
中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。注意:__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 s = Student() s.name='a' s.age=11 s.score=99 # AttributeError: 'Student' object has no attribute 'score' 复制代码
子类实例允许定义的属性就是自身的
__slots__
加上父类的__slots__
。
把一个方法变成属性调用:@property
装饰器
把一个getter方法变成属性,只需要加上
@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s = Student() s.score=11 s.score 复制代码
不写 @score.setter
的话就只能读这个属性
多重继承&MixIn
(⊙﹏⊙) 这个就和java的不同了,在java中只能单继承,多实现。 而在这个python中,直接在括号中添加对应的父类即可。
class Dog(Mammal, RunnableMixIn): pass 复制代码
上面这个例子在设计上也称为mixin
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
一般单继承的class名称不加后缀MixIn(如Mammal),这个多继承的点要加后缀MixIn如(RunnableMixIn),
Python自带了TCPServer
和UDPServer
这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn
和ThreadingMixIn
提供。
多线程模式的UDP服务:
class MyUDPServer(UDPServer, ThreadingMixIn): pass 复制代码
定制类
__str__()
,__repr__()
这两个同Java的toString()
方法,只不过这个__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,__repr__()
是为调试服务的
class Student(object): def __init__(self, name): self.name = name def __str__(self): # 2 return 'Student object (name=%s)' % self.name __repr__ = __str__ # 1 s=Student('ken') s # 2 print(s) # 1 Student object (name=ken) # 同时注释掉 # 1 的内容就可以看到效果 复制代码
__iter__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个
__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
这个博主之前的文章中也也有记录到:《python3入门笔记三之高级特性---切片,迭代,列表生成式,生成器,迭代器》
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 for n in Fib(): print(n) # 1 1 2 3 5 8...89 复制代码
__getitem__
- 按照下标取出元素
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a f = Fib() f[10] # 89 复制代码
- 切片
getitem()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:
class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop step = n.step print('step:',step) if start is None: start = 0 if step is None: step = 1 a, b = 1, 1 L = [] for x in range(stop): if x >= start: if(x%step==0): L.append(a) a, b = b, a + b return L f=Fib() print(f[:10]) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] print(f[:10:2]) # [1, 2, 5, 13, 34] print(f[10]) # 89 复制代码
__call__
任何类,只需要定义一个
__call__()
方法,就可以直接对实例进行调用。怎么判断一个变量是对象还是函数呢? 通过
callable()
函数,我们就可以判断一个对象是否是“可调用”对象。
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) s = Student('Michael') if callable(s): s() # My name is Michael. 复制代码
动态返回一个属性:__getattr__
这个在上篇笔记中有写到《python入门笔记六之面向对象编程》
class Student(object): def __getattr__(self, attr): if attr=='age': return lambda: 25 elif attr =='name': return 'Amy' s = Student() print(s.age()) # 25 注意这里返回的是一个lambda表达式,需要调用该方法才能拿到值 print(s.name) # Amy 复制代码
链式调用的例子 :
class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) def __call__(self, path): return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__ print(Chain().status.user.timeline.list) # /status/user/timeline/list # 注意这里的chain返回的是chain对象,所以这个users('michael')就可以很好地理解了。 print(Chain().users('michael').repos) # /users/michael/repos 复制代码
使用枚举类:Enum
枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value) # value属性则是自动赋给成员的int常量,默认从1开始计数。 # Jan => Month.Jan , 1 # Feb => Month.Feb , 2 # Mar => Month.Mar , 3 # . # . # . # Dec => Month.Dec , 12 复制代码
获取枚举常量的两种方式:
- 根据成员名称
- 根据value值
from enum import Enum, unique @unique # @unique装饰器可以帮助我们检查保证没有重复值。 class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 print(Weekday.Mon,'=',Weekday.Mon.value) # Weekday.Mon = 1 print(Weekday['Mon'],'=',Weekday.Mon.value) # Weekday.Mon = 1 print(Weekday(1),'=',Weekday.Mon.value) # Weekday.Mon = 1 for name, member in Weekday.__members__.items(): print(name, '=>', member) 复制代码
小测试: 把gender属性改造为枚举类型,避免使用字符串
class Gender(Enum): Male = 0 Female = 1 class Student(object): def __init__(self, name, gender): self.name = name self.gender = gender def __str__(self): return 'Student object name={},gender={}'.format(self.name,self.gender) __repr__=__str__ bart = Student('Bart', Gender.Male) print(bart) # Student object name=Bart,gender=Gender.Male bart # Student object name=Bart,gender=Gender.Male 复制代码
使用元类
type()
这个也在上篇博文讲到,现在要补充下它的其他知识点
type()
函数可以查看一个类型或变量的类型
type()
函数既可以返回一个对象的类型,又可以创建出新的类型
print(type(str)) # <class 'type'> str是class,所以类型是type print(type('123')) # <class 'str'> '123'是实例。所以类型是str def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) 复制代码
通过type()
函数创建出Hello类,而无需通过class Hello(object)
...的定义:
要创建一个class对象,type()函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
如图(通过这些ide可以帮助我们更快认识这个函数):
def fn(self, name='world'): return name Hello=type('Hello',(object,),dict(hello=fn)) # 创建出Hello类 h=Hello() # 注意这里才去创建Instance实例 print(h.hello('1')) print(type(Hello)) # <class 'type'> print(type(h)) # <class '__main__.Hello'> 复制代码
metaclass
除了使用
type()
动态创建类以外,要控制类的创建行为,还可以使用metaclass
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码 (已劝退😆 哈哈哈)
大概理解就是它可以定义创建类时候的行为。
# metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list, metaclass=ListMetaclass): pass L = MyList() L.add(1) L # 1 复制代码
一般写代码的时候用不上,使用metaclass
的典型例子是ORM框架(😱老底都揭露出来了)另外再写写这个例子😄