面向对象高级特性
静态方法
通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod #把eat变为静态方法
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
d = Dog('wangcai')
d.eat()
结果会报错
Traceback (most recent call last):
File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 103, in <module>
d.eat()
TypeError: eat() missing 1 required positional argument: 'self'
程序中调用eat()方法会报错,报错信息为缺少一个位置参数。把eat变为静态方法实际上跟类没什么关系,把这个方法和类的关联截断了,其实就是一个函数,只是需要通过类调用。那我们要如何调用呢?其实很简单,就是按照函数的传参把参数传进去
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
d = Dog('wangcai')
d.eat(d)
结果:
wangcai is eating bone
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod
def eat():
print("{} is eating {}".format('wangcai','bone'))
d = Dog('wangcai')
d.eat() #实例化后再调用
结果:
wangcai is eating bone
--------------------------------------------------------
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod
def eat():
print("{} is eating {}".format('wangcai','bone'))
Dog.eat() #实例化后再调用没什么意义。不实例化,直接调用
结果: wangcai is eating bone
类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量,为什么会这样呢?继续往下看
class Dog(object):
def __init__(self):
self.name = 'lx'
@classmethod
def eat(cls):
print("{} is eating {}".format(cls.name,'bone'))
d = Dog("aaaaa")
d.eat()
结果会报错: AttributeError: type object 'Dog' has no attribute 'name'
class Dog(object):
name = 'bbbb'
def __init__(self):
# self.name = 'lx'
pass
@classmethod
def eat(cls):
print("{} is eating {}".format(cls.name,'bone'))
d = Dog("aaaaa")
d.eat()
结果: bbbb is eating bone
类方法和静态方法的区别:两者都可以通过类来调用,类方法中的cls参数就是类本身,静态方法默认没用参数,相当于函数,只是通过类来调用,需要什么参数再传进去
class Dog(object):
name = 'bbbb'
def __init__(self):
# self.name = 'lx'
pass
@staticmethod
def run(cls):
print("{} running....".format(cls.name))
@classmethod
def eat(cls):
print(cls)
print("{} is eating {}".format(cls.name,'bone'))
Dog.run(Dog)
Dog.eat()
结果:
bbbb running....
<class '__main__.Dog'>
bbbb is eating bone
属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性
class Dog(object):
#name = 'bbbb'
def __init__(self,name):
self.name = name
@property
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
d = Dog("aaaaa")
d.eat()
结果会报错:
TypeError: 'NoneType' object is not callable
我们发现调用会报错, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
class Dog(object):
#name = 'bbbb'
def __init__(self,name):
self.name = name
@property
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
d = Dog("aaaaa")
d.eat
既然是属性,那就可以赋值,那么如何对属性赋值呢?可以使用@setter
class Dog(object):
#name = 'bbbb'
def __init__(self,name):
self.name = name
@property
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
@eat.setter
def eat(self,name):
self.name = name
d = Dog("aaaaa")
d.eat = 'ccccc' #实际转化为d.eat('ccccc')
d.eat #实际转化为d.eat()
结果:
ccccc is eating bone
既然是属性那应该可以删除。如果直接使用del d.eat删除,发现会报错:AttributeError: can't delete attribute.需使用@deleter方法
class Dog(object):
#name = 'bbbb'
def __init__(self,name):
self.name = name
@property
def eat(self):
print("{} is eating {}".format(self.name,'bone'))
@eat.setter
def eat(self,name):
self.name = name
@eat.deleter
def eat(self):
del self.name
print("删完了")
d = Dog("aaaaa")
d.eat = 'ccccc' #实际转化为d.eat('ccccc')
d.eat #实际转化为d.eat()
del d.eat #实际转化为d.eat()
d.eat
结果:
ccccc is eating bone
删完了

类的特殊成员方法
-
__doc__ 表示类的描述信息
class Dog(object):
"""
描述狗这个对象
"""
def __init__(self,name):
self.name = name
print(Dog.__doc__)
结果:
描述狗这个对象
-
__module__ 和 __class__
__module__ 表示当前操作的对象所属类在哪个模块
__class__ 表示当前操作的对象的类是什么
class Dog(object):
"""
描述狗这个对象
"""
def __init__(self,name):
self.name = name
from test1 import Dog
d = Dog('bbbb')
print(d.__module__)
print(d.__class__)
结果:
test1
<class 'test1.Dog'>
-
__init__ 构造方法,通过类创建对象时,自动触发执行。
-
__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
-
__call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Dog(object):
"""
描述狗这个对象
"""
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print("__call__")
d = Dog('aaaa')
d()
结果:__call__
-
__dict__ 查看类或对象中的所有成员
class Role(object):
school = '树人'
def __init__(self,name):
self.name = name
self.age = 27
d = Role('lxj')
print("实例化对象成员字典:",d.__dict__)
print("类的成员字典:",Role.__dict__)
结果:
实例化对象成员字典: {'name': 'lxj', 'age': 27}
类的成员字典: {'__dict__': <attribute '__dict__' of 'Role' objects>, '__init__': <function Role.__init__ at 0x006B0270>, '__weakref__': <attribute '__weakref__' of 'Role' objects>, '__doc__': None, 'school': '树人', '__module__': '__main__'}
-
__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值,不影响实例的调用。
class Role(object):
def __init__(self,name):
self.name = name
self.age = 27
def __str__(self):
return "str..."
d = Role('lxj')
print(d)
结果:
str...
-
__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):
def __init__(self):
self.data = { }
def __getitem__(self, key):
print('__getitem__', key)
return self.data.get(key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
self.data[key] = value
def __delitem__(self, key):
print('__delitem__', key)
del self.data[key]
obj = Foo()
obj['k2'] = 'alex' # 自动触发执行 __setitem__
result = obj['k2'] #自动触发执行 __getitem__
print("result is :{}".format(result))
del obj['k2'] #自动触发执行 __delitem__
结果:
__setitem__ k2 alex
__getitem__ k2
result is :alex
__delitem__ k2
-
__slots__
class Student(object):
pass
s = Student()
s.name = "Michael" #给实例绑定属性
print(s.name)
结果:
Michael
from types import MethodType
def set_age(self,age):
self.age = age
class Student(object):
pass
s = Student()
s.set_age = MethodType(set_age,s) #给实例绑定一个方法,只对该实例有用
s.set_age(27) #调用方法
print(s.age)
结果:
27
#给所有实例绑定方法可以用Student.set_age = set_age
#但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
#为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实#例能添加的属性:
class Student(object):
__slots__ = ('name','age')
pass
s = Student()
s.name = 'lxj'
s.age = 27
s.score = 99
结果:
Traceback (most recent call last):
File "D:/python_work/elective_system/test.py", line 39, in <module>
s.score = 99
AttributeError: 'Student' object has no attribute 'score'
#由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
#使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
-
__repr__
class Role(object):
def __init__(self, name,age):
self.name = name
self.age = age
def __str__(self):
return "({0.name},{0.age})".format(self)
def __repr__(self):
return 'Role({0.name},{0.age})'.format(self)
d = Role('lxj',21)
print("d is {!r}".format(d)) #显示repr
print("d is {!s}".format(d)) #显示str
结果:
d is Role(lxj,21)
d is (lxj,21)
#与__str的区别,__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。
#通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:
class Role(object):
def __init__(self, name,age):
self.name = name
self.age = age
def __str__(self):
return "({0.name},{0.age})".format(self)
__repr__ = __str__
d = Role('lxj',21)
print("d is {!r}".format(d)) #显示repr
print("d is {!s}".format(d)) #显示str
结果:
d is (lxj,21)
d is (lxj,21)
-
__getattr__
#首先来看个例子,当我们调用不存在的属性时,会报错
class Role(object):
def __init__(self, name):
self.name = name
s = Role("lxj")
print(s.name)
print(s.age)
结果:
lxj
Traceback (most recent call last):
File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 150, in <module>
print(s.age)
AttributeError: 'Role' object has no attribute 'age'
#那如何避免这个错误呢?1、可以加上一个score属性外;2、Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:
class Role(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
if item == 'age':
return 23
s = Role("lxj")
print(s.name)
print(s.age)
结果:
lxj
23
#当调用不存在的属性时,比如age,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值,也可以返回函数
#此时还有个问题,当我们调用其他不存在的属性,不会报错了,而是打印None
如:print(s.asd) -> None,我们有必要对上面的程序再优化下,当调用不存在的属性,继续报错
class Role(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
if item == 'age':
return 23
raise AttributeError("{self.__class__.__name__} object has no attribute {item}".format(self = self,item = item))
s = Role("lxj")
print(s.name)
print(s.age)
print(s.asd)
结果:
Traceback (most recent call last):
lxj
File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 157, in <module>
23
print(s.asd)
File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 151, in __getattr__
raise AttributeError("{self.__class__.__name__} object has no attribute {item}".format(self = self,item = item))
AttributeError: Role object has no attribute asd
这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。
这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。
-
__new__ \ __metaclass__
class Foo(object):
def __init__(self,name):
self.name = name
f = Foo("lxj")
上述代码中,f是通过 Foo 类实例化的对象,其实,不仅f是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:f对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的构造方法创建。
print type(f) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
类创建方式
1、普通方式
class Foo(object):
def func(self):
print("hello lxj")
2、特殊方式
def func(self):
print("hello lxj")
Foo = type('Foo',(object,),{'func':func})
#type第一个参数为类名
#type第二个参数为当前类的基类,为元组形式
#type第三个参数为类的成员,为字典形式
1 def func(self): 2 print("hello {}".format(self.name)) 3 4 def __init__(self,name,age): 5 self.name = name 6 self.age = age 7 8 Foo = type('Foo',(object,),{'func':func,'__init__':__init__}) 9 10 11 a = Foo('lxj',27) 12 a.func()
类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__(元类),其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
class Foo(object):
#__metaclass__ = MyType
def __init__(self,name):
self.name = name
print("Foo __init__")
def __new__(cls, *args, **kwargs):
print("Foo __new__")
return object.__new__(cls) #继承父亲的__new__方法
Foo('lxj')
结果:
Foo __new__
Foo __init__
#此时我们可以得出结论__new__在__init__之前运行,现在我们注释掉return object.__new__(cls)
def __new__(cls, *args, **kwargs):
print("Foo __new__")
#return object.__new__(cls)
a = Foo('lxj')
print(a)
结果:
Foo __new__
None
#此时我们发现根本就没有实例化,我们可以得出结论new是用来创建实例的
class MyType(type):
def __init__(self,what,bases=None,dict=None):
print("MyType __init__")
super().__init__(what,bases,dict)
def __call__(self, *args, **kwargs):
print("MyType __call__")
obj = self.__new__(self,*args,**kwargs)
self.__init__(obj,*args,**kwargs)
class Foo(object):
__metaclass__ = MyType
def __init__(self,name):
self.name = name
print("Foo __init__")
def __new__(cls, *args, **kwargs):
print("Foo __new__")
return object.__new__(cls)
a = Foo('lxj')
#在python3上应该这么写class Foo(object,metaclass=MyType),运行结果与python2一致
MyType __init__
MyType __call__
Foo __new__
Foo __init__

关于元类的使用:https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python?answertab=votes#tab-top
更多定制类用法

浙公网安备 33010602011771号