1. property动态属性
>>> from datetime import date, datetime
>>> class User:
>>> def __init__(self, name, birthday):
>>> self.name = name
>>> self.birthday = birthday
>>> self._age = 0
>>> @property # get值
>>> def age(self):
>>> return datetime.now().year - self.birthday.year
>>> @age.setter # set值
>>> def age(self, value):
>>> self._age = value
>>> if __name__ == '__main__':
>>> user = User('cannon', date(year=1993, month=6, day=7))
>>> print(user.age)
25
>>> user.age = 99
>>> print(user._age)
99
2. __ getattr __ 、__ getattribute __魔法函数
__ getattr __:查找不到属性的时候 调用
>>> from datetime import date
>>> class User:
>>> def __init__(self, name, birthday):
>>> self.name = name
>>> self.birthday = birthday
>>> self._age = 0
>>> def __getattr__(self, item): #__getattr__ 查找不到属性的时候 调用
>>> return 'not find attr'
>>> if __name__ == '__main__':
>>> user = User('cannon', date(year=1993, month=6, day=7))
>>> print(user.age) # 调用__getattr__
not find attr
__ getattribute __:# 查找属性时 无条件进入, 把持所有 属性的入口。 能不重写尽量不要重写
>>> from datetime import date
>>> class User:
>>> def __init__(self, name, birthday):
>>> self.name = name
>>> self.birthday = birthday
>>> self._age = 0
>>> def __getattr__(self, item): #__getattr__ 查找不到属性的时候 调用
>>> return 'not find attr'
>>> def __getattribute__(self, item): # 查找属性时 无条件进入, 把持所有 属性的入口。 能不重写尽量不要重写
>>> return 'cannon'
>>> if __name__ == '__main__':
>>> user = User('cannon', date(year=1993, month=6, day=7))
>>> print(user.age) # 调用__getattribute__
cannon
3. 属性描述符和属性查找过程
假设类中的属性,你想规定它只能是 int类型,如何高效地做到这一点?
我们需要属性描述符
>>> import numbers
>>> class IntField: # __get__ __set__ __delete__中实现任意一个就会变成属性描述符
>>> def __get__(self, instance, owner):
>>> return self.value
>>> def __set__(self, instance, value):
>>> if not isinstance(value, numbers.Integral):
>>> raise ValueError('int value need')
>>> elif value < 0:
>>> raise ValueError('value should > 0')
>>> self.value = value
>>> def __delete__(self, instance):
>>> pass
>>> class User:
>>> age = IntField()
>>> if __name__ == '__main__':
>>> user = User()
>>> user.age = 30 # age是属性描述符,所以__dict__为空。 否则会放入__dict__中
>>> print(user.__dict__)
{}
>>> print(user.age) # age是属性描述符, 他会优先调用__get__, 而不是从__dict__中去取值
30
>>> user.age = 'abc' # 不是指定的int类型,就会报错
Traceback (most recent call last):
File "3_attr_desc.py", line 63, in <module>
user.age = 'abc'
File "3_attr_desc.py", line 10, in __set__
raise ValueError('int value need')
ValueError: int value need
详解属性描述符执行时的调用顺序
如果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”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
(3)如果“age”出现在User或其基类的__dict__中
(3.1)如果age是non-data descriptor(非数据描述符),那么调用其__get__方法, 否则
(3.2)返回 __dict__[‘age’]
(4)如果User有__getattr__方法,调用__getattr__方法,否则
(5)抛出AttributeError
4. __new__和__init__的区别
>>> class User:
>>> def __new__(cls, *args, **kwargs):
>>> print('in new')
>>> def __init__(self):
>>> print('in init')
>>> if __name__ == '__main__':
>>> user = User(name = 'cannon')
in new
上面代码中 可以看出 没有调用__init__, 为什么呢?
答:
__new__ 用于控制对象的生成过程, 在对象生成之前
__init__ 是用来完善对象的
如果__new__不返回对象 , 就不会调用__init__
代码举例:
>>> class User:
>>> def __new__(cls, *args, **kwargs):
>>> print('in new')
>>> return super(User, cls).__new__(cls)
>>> def __init__(self, name): # 有name参数传入,这里必须一致
>>> print('in init')
# new 用于控制对象的生成过程, 在对象生成之前
# init 是用来完善对象的
# 如果new不返回 对象 , 就不会调用init
>>> if __name__ == '__main__':
>>> user = User(name = 'cannon')
in new
in init
__new__一般情况不需要重写, 但在框架中会经常出现
5. 自定义元类metaclass
类也是对象 type是能创建类的类
1. 首先我们也来自己写一个 能动态创建类的函数:
>>> 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') # Myclass 是动态创建的User类
>>> my_obj = Myclass() # my_obj是User类的实例化
>>> print(my_obj)
user
2. 我们可以直接通过type 动态创建类:
# type动态创建类
>>> def say(self):
>>> return self.name
>>> class BaseClass:
>>> def answer(self):
>>> return 'i am baseclass'
# BaseClass 后面的, 不能漏。 say 后面不能加()
>>> User = type('User', (BaseClass, ), {'name':'user', 'say':say})
>>> my_obj = User()
>>> print(my_obj.name)
>>> print(my_obj.say())
>>> print(my_obj.answer())
type的__init__函数(pycharm自动提炼的),可以明白看出应传入的参数。
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
3. type创建类比较少用,最常用的方法是 通过metaclass的方法来创建
python的 实例化过程
1. 找metaclass (父类中也会去找),通过metaclass创建类
2. 没metaclass(包括所有父类中也没有),那么通过type 创建User的类对象
>>> count = 1
>>> class MetaClass(type): # 元类要继承type
>>> def __new__(cls, *args, **kwargs):
>>> global count
>>> print('metaclass', count)
>>> count += 1
>>> return super().__new__(cls, *args, **kwargs)
>>> class BaseClass(metaclass=MetaClass):
>>> def answer(self):
>>> return 'i am baseclass'
# 如果BaseClass中没有metaclass,这里会用type去创建.
# 这里BaseClass中有metaclass, 所以metaclass优先
>>> class User(BaseClass):
>>> def __init__(self, name):
>>> self.name = name
>>> def __str__(self):
>>> return 'User'
>>> if __name__ == '__main__':
>>> my_obj = User(name='cannon')
>>> print(my_obj.answer())
metaclass 1
metaclass 2
i am baseclass
User
6. 通过元类实现orm
综合运用自定义元类 和 属性描述符,实现类似django的orm
import numbers
class Field:
pass
class IntField(Field):
def __init__(self, db_column, min_value=None, max_value=None):
self._value = None
self.min_value = min_value
self.max_value = max_value
self.db_column = db_column
if min_value is not None:
if not isinstance(min_value, numbers.Integral):
raise ValueError('min_value must be int')
elif min_value < 0:
raise ValueError('min_value must be positive int')
if max_value is not None:
if not isinstance(max_value, numbers.Integral):
raise ValueError('max_value must be int')
elif max_value < 0:
raise ValueError('max_value must be positive int')
if min_value is not None and max_value is not None:
if min_value > max_value:
raise ValueError('min_value must be smaller than max_value')
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, numbers.Integral):
raise ValueError('int value need')
elif value > self.max_value or value < self.min_value:
raise ValueError('value must between min_value and max_value')
self._value = value
class CharField(Field):
def __init__(self, db_column, max_length=None):
self._value = None
self.db_column = db_column
if max_length is None:
raise ValueError(' you must spcify max_length for chaifield')
self.max_length = max_length
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError('string value need')
elif len(value) > self.max_length:
raise ValueError('value len excess max_length')
self._value = value
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs, **kwargs):
if name == 'BaseModel':
return super().__new__(name, bases, attrs, **kwargs)
fields = {}
for key, value in attrs.items():
if isinstance(value, Field):
fields[key] = value
attrs_meta = attrs.get('Meta', None)
_meta = {}
db_table = name.lower()
if attrs_meta is not None:
table = getattr(attrs_meta, 'db_table', None)
if table is not None:
db_table = table
_meta['db_table'] = db_table
attrs['_meta'] = _meta
attrs['fields'] = fields
del attrs['Meta']
return super().__new__(cls, name, bases, attrs, **kwargs)
class BaseModel(metaclass=ModelMetaClass):
def _init__(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
return super().__init__()
def save(self):
fields = []
values = []
for key, value in self.fields.items():
db_column = value.db_column
if db_column is None:
db_column = key.lower()
fields.append(db_column)
value = getattr(self, key)
values.append(str(value)) # age是int 这里也得统一成str
sql = "insert {db_table}({fields}) value({values})".format(
db_table=self._meta['db_table'], fields=','.join(fields), values=','.join(values))
pass
class User(BaseModel):
name = CharField(db_column="name", max_length=10)
age = IntField(db_column="age", min_value=0, max_value=100)
class Meta:
db_table = 'user'
if __name__ == '__main__':
user = User()
user.name = 'cannon'
user.age = 25
user.save()