1. property动态属性
‘’’A文件的调用语句写在main函数外部,B文件importA文件的函数时时会自动调用函数’’’
Python programming provides us with a built-in @property decorator which makes usage of getter and setters much easier in Object-Oriented Programming.
1.1 class without getters and setters
a class that stores the temperature in degress Celsius
class Celsius:def __init__(self,temperature=0):self.temperature = temperaturedef to_fahrenheit(self):return (self.temperature*1.8)+32if __name__ == '__main__':'''python设置属性和获取属性的基本方法'''# create a new projecthuman = Celsius()human.temperature = 37# get attrprint(f'get the human temperature attribute {human.temperature}')# call the methodprint(f"get the human to_fahrenheit method {round(human.to_fahrenheit(),2)}")'''get the human temperature attribute 37get the human to_fahrenheit method 98.60000000000001'''
每当我们temperature如上所述分配或检索任何对象属性时,Python都会在对象的内置__dict__字典属性中进行搜索。
print(human.dict) # {‘temperature’: 37}
1.2 class with getters and setters
假设我们要扩展 temperature上面定义的类。我们知道任何物体的温度都不能低于-273.15摄氏度(热力学中的绝对零)
让我们更新代码以实现此值约束。
上述限制的一个明显解决方案是隐藏属性temperature(将其设为私有),并定义新的getter和setter方法来对其进行操作。可以按以下步骤完成:
class Celsius:
def __init__(self,temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature()*1.8)+32
# getter
def get_temperature(self):
return self._temperature
# setter
def set_temperature(self,value):
if value <-237.15:
raise ValueError("temperature below -273.5 is wrong!")
self._temperature = value
if __name__ == '__main__':
human = Celsius(37)
# get attr via a getter
print(human.get_temperature()) # 37
# get method by the method itself
print(round(human.to_fahrenheit(),2)) # 98.6
# new constraint implementation
human.set_temperature(-300) # ValueError: temperature below -273.5 is wrong!
# Get the to_fahreheit method
print(human.to_fahrenheit())
如我们所见,以上方法引入了两个new get_temperature()和set_temperature()方法。
此外,temperature被替换为_temperature。_开头的下划线用于表示Python中的私有变量。
然而这种方法让我们实现所有我们之前的类的程序都必须修改obj的代码。在代码量多的项目中重构代码可能会出现问题。既代码不能向后兼容,因此需要@property。
1.3 the @property decorator
property() 是一个python的内置函数,用于创建和返回一个property对象。
property(fget=None,fset=None,fdel=None,doc=None)
'''
fget 是获取属性值的函数
fset 是设置属性值的功能
fdel 是删除属性的功能
doc 是一个字符串(如评论)
'''
>>> property()
<property object at 0x0000000003239B38>
对象的属性有三种方法,getter(),setter(),和deleter()。指定fget,fset和fdel
temperature = property(get_temperature, set_temperature)
和下面的代码是等效的
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
上述的代码结构可以应用成decorator。我们甚至不需要定义get_temperature()和set_temperature()函数,因为他们不需要而且会污染命名空间。
只需要使用decorator重用temperature的同样的名字来定义getter()和setter()
class Celsius:
def __init__(self,temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return self.temperature*1.8 +32
@property
def temperature(self):
print('getting value...')
return self._temperature
@temperature.setter
def temperature(self,value):
print('setting value...')
if value < -273.15:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
if __name__ == '__main__':
human = Celsius(37)
'''
>>> setting value...
'''
print(human.temperature)
'''
>>> getting value...
>>> 37
'''
print(human.to_fahrenheit())
'''
>>> getting value...
>>> 98.60000000000001
'''
coldest_thing = Celsius(-300)
'''
>>> setting value...
>>> ValueError: Temperature below -273 is not possible
'''
2. __getattr__、__getattribute__魔法函数
__getattr__(self,attr)
- 触发时机:获取不存在的对象成员时触发
- 参数:1、接收当前对象的self,2、获取成员名称的字符串
- 返回值: 必须有值
- 作用:为访问不存在的属性设置值
- 注意:
__getattribute__(self,attr)无论何时都会在__getattr__(self,attr)之前触发,触发了__getattribute__(self,attr)有返回值就不会在触发__getattr__(self,attr)
__getattribute__(self,attr)
- 触发时机:使用对象成员时触发,无论成员是否存在
- 参数:1、接收当前对象的self,2、获取成员名称的字符串
- 返回值: 必须有值
- 作用:在具有封装操作(私有化时),为程序开部分访问权限使用
当获取正确属性名称的属性时,触发了__getattribute__(self,attr),要访问对象的name成员。
写错类的属性名称时Name.
from datetime import time,date
class Person:
def __init__(self,name,birthday,info={}):
self.Name =name
self.birthday = birthday
self.info = info
if __name__ == '__main__':
p = Person('ck',date(year=1996,month=4,day=29),info={'company':'cmb'})
print(p.Name) # ck 触发了__getattribute__(),要访问对象的name成员
print(p.name) # AttributeError: 'Person' object has no attribute 'age'
添加 __getattr__(self, attr)
def __getattr__(self, item):
return f'not find attr: {item}'
>>>not find attr: name
3. 属性描述符和属性查找过程
属性描述符对象:只要实现 get set delete 这3个当中的任何一个方法,这个对象就是一个属性描述符的对象; 通过属性描述符,可以控制赋值时的一些行为。
import numbers
class IntField():
# 数据属性描述符
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
# 检查参数是否是int类型
if not isinstance(value, numbers.Integral):
raise ValueError("int value need")
if value < 0:
raise ValueError("positive value need")
# 如果value值是int,需要将这个值保存起来
# 此时不能将值保存在instance里,比如 instance.age = value,如果这样保存,此instance又会调用IntField的 __set__方法,
# 这样就陷入死循环,这里将值保存在IntField class里,所以是将值放在self里
# 因为是将值放在了 IntField类里,所以__get__时,需要将它的值 return回去
self.value = value
def __delete__(self, instance):
pass
class NonDataField:
# 只实现一个 __get__ 方法,称为: 非数据属性描述符
def __get__(self, instance, owner):
return self.value
class User:
age = IntField()
if __name__ == '__main__':
user = User()
user.age = 30 # 当对user对象设置age属性时,它会调用IntField类的 __set__ 方法, 所以当需要对设置的属性值做些检查时,可以把逻辑写在__set__方法中
print(user.age)
# user.age = "abc" # 会抛出异常
# print(user.age)
“”” 属性查找过程 如果user是某个类的实例,那么user.age(以及等价的getattr(user, ‘age’))首先调用getattribute。 如果类定义了getattr方法,那么在getattribute抛出AttributeError的时候就会调用到getattr , 而对于描述符(get)的调用,则是发生在getattribute内部的。
user = User(),那么user.age 顺序如下:
(1) 如果”age”是出现在User或其基类的dict中, 且age是data descriptor(数据属性描述符), 那么调用其get方法,否则
(2) 如果”age”出现在obj(这里指user对象)的dict中, 那么直接返回obj.dict[“ages”], 否则
(3) 如果”age”出现在User或其基类的dict中
(3.1) 如果age是non-data descriptor(非数据属性描述符),那么调用其__get__方法,否则 (3.2) 返回__dict__["age"](4) 如果User有getattr方法,调用getattr方法,否则
(5) 抛出AttributeError
“””
4. __new__ 和__init__的区别
__new__是一个静态方法,__init__是一个实例方法__new__方法返回一个创建的实例,而__init__什么都不返回- 只有在
__new__返回一个cls的实例时后面的__init__才能被调用 - 当创建一个新实例时调用
__new__,初始化一个实例时用__init__
ps: __metaclass__在创建类时起作用,我们可以分别用__metaclass__、__new__和__init__来分别在类创建、实例创建和实例初始化的时候做一些工作。
class User:
def __init__(self,name):
print('in init')
self.name = name
def __new__(cls, *args, **kwargs):
print("in new")
return super().__new__(cls)
'''
new是用来控制对象的生成过程,在对象生成之前
init是用来完善对象的
'''
if __name__ == '__main__':
user = User(name='ck')
>>>in new
in init
class User:
def __init__(self, name):
print('in init')
self.name = name
def __new__(cls, *args, **kwargs):
print('in new')
# return super().__new__(cls)
'''
如果new不返回对象,则不会调用init函数
'''
if __name__ == '__main__':
user =User(name='ck')
>>>in new
5. 自定义元类
元类是创建类的类 .元类的主要用途是创建API
先来看一个简单的动态创建类的函数
https://jeza-chen.com/2020/01/09/Python3-Metaclass/
'''
类也是对象,type创建类的类
'''
'''动态创建类'''
def create_class(name):
if name == 'User':
class User:
def __str__(self):
return 'user'
return User
elif name == 'Company':
class Company:
def __str__(self):
return 'company'
return Company
if __name__ == '__main__':
MyClass = create_class('User')
my_obj = MyClass()
print(my_obj)
print(type(my_obj)) # <class '__main__.create_class.<locals>.User'>
上面的函数实现逻辑很僵硬不灵活。我们可以用type实现灵活的动态创建类。type是元类。
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict) # type(name,基类,属性)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
''':type动态创建类'''
if __name__ == '__main__':
# MyClass = create_class('User')
MyClass = type("User", (), {}) # () {}表示不继承基类,只继承object,{}表示先不添加属性
my_obj = MyClass()
print(my_obj) # <__main__.User object at 0x7f0b8c34b940>
print(type(my_obj)) # <class '__main__.User'>
''':type动态创建类'''
def say(self):
return "i am user method : name = " + self.name
class BaseClass:
def answer(self):
return 'i am base class'
if __name__ == '__main__':
# MyClass = create_class('User')
MyClass = type("User", (BaseClass,), {"name":"user","say":say})
'''
(BaseClass,)中的 ","不能少
'''
my_obj = MyClass()
print(my_obj.name) # user
print(my_obj.say) # <bound method say of <__main__.User object at 0x7f4b18e74a20>>
print(my_obj.say()) # i am user method : name = user
print(my_obj.answer()) # i am base class
使用metaclass创建元类
'''
什么是元类,元类是创建类的类,对象<-class(对象)<-type
'''
class ThisIsMetaClass(type): # 需要继承type
pass
class User(metaclass=ThisIsMetaClass):
pass
python中类的实例化过程,会首先寻找metaclass属性,如果找到,通过metaclass去创建User类。找不到就去基类找,基类中没有去模块中找。
type去创建类对象,实例化
class ThisIsMetaClass(type): #需要继承type
def __new__(cls, *args, **kwargs):
pass
class BaseClass(metaclass=ThisIsMetaClass):
def answer(self):
return 'i am base class'
class User(BaseClass):
pass
my_obj = User()
cls = {type} <class ’__main__.ThisIsMetaClass'>

