本章包含:_slots_、@property、多重继承、定制类、枚举类、元类
参考:廖雪峰python
实例可以动态添加方法,使用types库的 Methodtype
class Student():
pass
s = Student()
s1 = Student()
s.name = 10
print(s.name)
from types import MethodType
def haha(self):
print('haha')
s.haha = MethodType(haha,s)
s.haha()
s1.haha() #会报错 AttributeError
但是实例动态添加方法只是针对独立的实例,可为类动态添加方法
class Student():
pass
def haha(self):
print('haha')
from types import MethodType
Student.haha = MethodType(haha,Student)
s1 = Student()
s2 = Student()
s1.haha()
s2.haha()
使用__slots__(xx)
可限制 可变变量。但只对该类有用,子类的slot包括了子类本身slot和父类的slot。
class Student(object):
__slots__ = ('name', 'age')
pass
s = Student()
s.name = 10
s.age = 10
#s.haha = 10 # 会报错 AttributeError
@property和 @xxx.setter 既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。设置了@property就可以设置@xxx.setter
class Student():
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self): #只可读,修改会报错
return 2022 - self._birth
s = Student()
s.birth=2000
print(s.birth)
print(s.age)
#2000
#22
定义如下
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。可命名为Mixln
直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
class Student():
def __init__(self,name):
self.name = name
def __str__(self):
return "Student's name is {}".format(self.name)
def __repr__(self):
return "Student's name is {} (repr version)".format(self.name)
s = Student('Jeff')
print(s)
s
#Student's name is Jeff
#Student's name is Jeff (repr version)
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象(自身),然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
class Mieee():
def __init__(self):
self.mie = 0
def __iter__(self):
return self
def __next__(self):
self.mie +=1
if self.mie>20:
raise StopIteration()
return self.mie
m = Mieee()
for i in m:
print(i,end=' ')
#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:
使用__getitem__
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
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
m = Mieee()
print(m[5])
print(m[5:10])
#8
#[8, 13, 21, 34, 55]
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')
来尝试获得属性,这样,我们就有机会返回score的值.
只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name,不会在__getattr__
中查找.
注意,任意调用其他不在getattr的属性都会返回None,因为getattr默认返回None,如果想只对特定几个有相应,要跑出AttributeError
class Mie():
def __init__(self):
self.name = 'Jeff'
def __getattr__(self,attr):
if attr == 'score':
return 99 #属性
if attr == 'age':
return lambda:88 #方法
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
m = Mie()
print(m.score)
print(m.age())
#99
#88
__call__()
调用实例本身
还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
class Mie():
def __call__(self):
print('You are diaoyonging yourself')
m = Mie()
m()
#You are diaoyonging yourself
当我们需要定义常量时,更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum
类来实现这个功能
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)
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
也可以继承派生出自定义类,unique装饰器判断是否有重复
from enum import Enum, unique
@unique
class Age(Enum):
Jeff = 22
Jack = 21
Jason = 20
Rain = 19
Joa = 18
J1 = Age.Jeff
print(J1)
print(J1.value)
#Age.Jeff
#22
当在一个文件中创建类,另一个文件中调用时,如下
# hello.py
class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
# main.py
from hello import Hello
h = Hello()
h.hello()
Hello, world.
print(type(Hello))
#
print(type(h))
#
可见Hello类型为type,实例类型为hello.Hello,可以通过函数type()
创建这样一个类
def fun(self,name='Jeff'):
print('hello {0} '.format(name))
Hello = type('Hello',(object,),dict(hello=fun))#创建Hello class
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
#hello Jeff
#
#
type
第一个参数为类名,第二个参数为父类名(单元素写法有逗号),第三个参数为方法名字和函数绑定。