property动态属性
动态属性的设置
和动态属性的获取
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name=name
self.birthday=birthday
self._age=0
#user= User("xiaopang", date(year=1996, month=3, day=8))
# print("in{}file".format(__file__))
# def get_age(self):
# return datetime.now().year-self.birthday.year
@property #@property获取age属性
def age(self):
return datetime.now().year-self.birthday.year
# @age.getter
# def age(self):
# return self._age
@age.setter
def age(self,value):
self._age=value
# @age.setter
# def age_set(self):
# return 23
if __name__=="__main__":
user=User("xiaopang",date(year=1996,month=3,day=8))
# inI:/ainlp/pythonHight/chapter08/property_test.pyfile
print("in{}file".format(__file__))
# print(user.get_age()) ##23
print(user.age) #23
user.age=23
print(user.age)
property源码:
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
# (copied from class doc)
"""
pass
__getattr__和__getattribute__魔法函数
- __ getattr __ ():
#__getattr__,getattribute__
#__getattr__就是在查找不到属性的时候调用
from datetime import date
class User:
def __init__(self,name,birthday,info={}):
self.Name=name
self.birthday=birthday
self.info=info
#如果可以查找到某个属性,则不会调用这个函数,
#否则就会执行这个函数
# def __getattr__(self, item):
# # print("not find attr")
# # return self.Name
# return self.info[item]
if __name__=="__main__":
user=User("xiaopang",date(year=1996,month=3,day=8),info={"company_name":"yuzhou"})
#如果不存在属性 AttributeError: 'User' object has no attribute '_age'
# 但是如果在类里面加入__getattr__这个魔法函数不会报错,会执行
#这个函数里面的逻辑
# print(user._age)# not find attr None
# print(user.name)
print(user.company_name) #有__getattr__ 输出结果为 yuzhou
# 如果里面没有__getattr__,则会报错
#AttributeError: 'User' object has no attribute 'company_name'
- __ getattribute__()里面实现了__getattr__()
注意: __ getattribute__()这个函数如果要重写
会修改类里面的执行顺序,容易造成混乱,需要自己搞清楚里面的
内在逻辑
class User:
def __init__(self,info={}):
self.info=info
def __getattribute__(self, item):
return " i miss you"
if __name__=="__main__":
user=User(info={"company_name":"yuzhou","name":"xiaopang","age":23})
print(user.company_name)#i miss you
print(user.name)#i miss you
print(user.age)#i miss you
#说明了__ getattribute __ ()实在 __ init __ ()初始化之后
不管有查找到属性都会进入的魔法函数:\
属性描述符和属性查找过程
属性描述符:
只要实现__get__、set、__delete__方法中的一个就可以认为是描述符;
只实现__get__方法的对象是非数据描述符,在初始化之后它们只能被读取;
同时实现__get__和__set__的对象是数据描述符,这种属性是可读写的。
from datetime import date,datetime
import numbers
'''
只要实现__get__、set、__delete__方法中的一个就可以认为是描述符;
只实现__get__方法的对象是非数据描述符,在初始化之后它们只能被读取;
同时实现__get__和__set__的对象是数据描述符,这种属性是可读写的。
'''
#一个类里如果包含下面__get__、__set_、___delete__
#这3个魔法函数当中的任意一个函数,那么这个类就
# 是属性描述符
class DataIntField: #数据描述符
def __get__(self,instance,owner):
return self.value
def __set__(self, instance, value):
#判断age是否是数字类型的
if not isinstance(value,numbers.Integral):
raise ValueError("int value need")
if value<0 :
raise ValueError("age must be More than 0")
else:
self.value=value
def __delete__(self, instance):
pass
class NonDataIntField: #非数据属性描述符
def __get__(self,instance,owner):
return self.value
class User:
#age是一个属性描述符的对象
# age=DataIntField()
age=NonDataIntField()
'''
属性的查找顺序:
如果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
'''
# class User:
# def __init__(self,name,email,birthday):
# self.name=name
# self.email=email
# self.birthday=birthday
# self._age=0
# #user= User("xiaopang", date(year=1996, month=3, day=8))
# # print("in{}file".format(__file__))
# # def get_age(self):
# # return datetime.now().year-self.birthday.year
# @property #@property获取age属性
# def age(self):
# return datetime.now().year-self.birthday.year
# @age.getter
# def age(self):
# return self._age
# @age.setter
# def age(self,value):
# #检查是否是字符串类型
# self._age=value
#
#
# # @age.setter
# # def age_set(self):
# # return 23
if __name__=="__main__":
# user=User("xiaopang",date(year=1996,month=3,day=8))
# # inI:/ainlp/pythonHight/chapter08/property_test.pyfile
# print("in{}file".format(__file__))
# # print(user.get_age()) ##23
# print(user.age) #23
# user.age=23
# print(user.age)
user=User()
# user.age=30
# user.age=-1
# print(user.age)
# print(getattr(user,"age")) #30 等价于user.age
# print(user.__dict__)
# print(user.age)
print("..............................")
user.__dict__["age"]="xiaopang"
print(user.__dict__)
print(user.age)
pass
'''
'''
user. __ dict __ 这个属性是属于实例的 这样获取属性的顺序逻辑和通过使用
user.age这种逻辑是不一样的。
user. __ dict__直获取那个__dict__属性值,而不会走user.age查找顺序的流程。
user.age这种逻辑是不一样的,这里会直接寻找 __ getattribue __ 里面的
逻辑,
1.首先会寻找类或基类里面的数据属性属性描述符,如果没有
2.找到则实例的属性当中寻找,如果么没有
有则会寻找__getattr__
3.类或基类当中的属性__dict__当中
3.1非数据描述符,那么__get__
3.2返回 __ dict __ [‘age’]
如果都没有找到
4.如果User有__getattr__方法,调用__getattr__方法,否则
抛出异常抛出AttributeError
‘’’
属性的查找顺序:
如果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
‘’’
__ new __ 和 __ init __
class User:
#python 2.2之后
#new是用来控制对象的生成过程,在对象生成前
#init是用来完善对象的
#如果new方法不返回对象则不调用init函数
def __new__(cls,*args,**kwargs):
print("in new")
return super().__new__(cls)
def __init__(self,name):
self.name=name
if __name__ =="__main__":
user =User(name="xiaopang")
自定义元类
元类的概念:元类就是创建类的类。
对象 < – class(对象) < – type
首先考虑如何动态的创建类:
在这里插入代码片
#类也是对象,type是创建类的类
#动态的创建类,如何来做?
def create_class(name):
if name=="user":
class User:
# def __str__(self):
# return "user"
def createName(self):
return "xiaopang"
return User
elif name=="companpy":
class Company:
def __str__(self):
return "companpy"
return Company
if __name__=="__main__":
MyClass=create_class("user")
myjob=MyClass()
print(myjob.createName())
上面代码的写法还是需要我们去写class语句,稍微麻烦些,如何
让它更简洁呢?涉及到type了 另外在python当中 type是可以用来
创建类的
user=type(“类名”,(继承的基类,…),{“包含的属性”:“属性值”,…})
#如何定义使用type创建的类中的方法
def say(self):
return " xiaopang is an eater"
#type使用type动态创建类
# User=type("User",(),{})
if __name__=="__main__":
User = type("User", (), {"name":"xiaopang","say":say})
myjob=User()
print(User)#<class '__main__.User'>
print(myjob)#<__main__.User object at 0x0000000002D90128>
print(myjob.name)#xiaopang
#调用它里面的方法
print(myjob.say()) # xiaopang is an eater
#如何定义使用type创建的类中的方法
def say(self):
return " xiaopang is an eater"
#让动态创建的类继承Baseclass这个类
class Baseclass(object):
def answer(self):
return " i like xiaopang"
#type使用type动态创建类
# User=type("User",(),{})
if __name__=="__main__":
# MyClass=create_class("user")
# myjob=MyClass()
# print(myjob.createName())
User = type("User", (Baseclass,), {"name":"xiaopang","say":say})
myjob=User()
print(User)#<class '__main__.User'>
print(myjob)#<__main__.User object at 0x0000000002D90128>
print(myjob.name)#xiaopang
#调用它里面的方法
print(myjob.say()) # xiaopang is an eater
print(myjob.answer()) #i like xiaopang
在python3当中可以这样使用:
class MetaClass(type):
pass
class User(metaclass=MetaClass):
pass
MataClass创建的目的就是为了控制User这个类实例化的过程,为什么
它能够控制类的实例化过程呢?
python中类的实例化过程:
在默认情况下type去创建类对象,如果这个类中使用了元类,在类的实例化的过程中那么会首先寻找元类,通过元类(metaclass)去创建User类 。
class MetaClass(type):
def __new__(cls, *args, **kwargs):
return super().__new__(cls,*args, **kwargs)
pass
class User(metaclass=MetaClass):
#MataClass创建的目的就是为了控制User这个类实例化的过程
def __init__(self,name):
self.name=name
def __str__(self):
return "user"
if __name__=="__main__":
user=User(name="xiaopang")
print(user)#<__main__.User object at 0x0000000002D76780>
print(user.name) #xiaopang
pass
查看源码:
from collections.abc import *
from _collections_abc import all
Iterable
class Iterable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
return _check_methods(C, "__iter__")
return NotImplemented
ABCMeta这个类的源码:
class ABCMeta(type):
"""Metaclass for defining Abstract Base Classes (ABCs).
Use this metaclass to create an ABC. An ABC can be subclassed
directly, and then acts as a mix-in class. You can also register
unrelated concrete classes (even built-in classes) and unrelated
ABCs as 'virtual subclasses' -- these and their descendants will
be considered subclasses of the registering ABC by the built-in
issubclass() function, but the registering ABC won't show up in
their MRO (Method Resolution Order) nor will method
implementations defined by the registering ABC be callable (not
even via super()).
"""
# A global counter that is incremented each time a class is
# registered as a virtual subclass of anything. It forces the
# negative cache to be cleared before its next use.
# Note: this counter is private. Use `abc.get_cache_token()` for
# external code.
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace, **kwargs):
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
# Compute set of abstract method names
abstracts = {name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False)}
for base in bases:
for name in getattr(base, "__abstractmethods__", set()):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
cls.__abstractmethods__ = frozenset(abstracts)
# Set up inheritance registry
cls._abc_registry = WeakSet()
cls._abc_cache = WeakSet()
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
return cls
def register(cls, subclass):
"""Register a virtual subclass of an ABC.
Returns the subclass, to allow usage as a class decorator.
"""
if not isinstance(subclass, type):
raise TypeError("Can only register classes")
if issubclass(subclass, cls):
return subclass # Already a subclass
# Subtle: test for cycles *after* testing for "already a subclass";
# this means we allow X.register(X) and interpret it as a no-op.
if issubclass(cls, subclass):
# This would create a cycle, which is bad for the algorithm below
raise RuntimeError("Refusing to create an inheritance cycle")
cls._abc_registry.add(subclass)
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
return subclass
def _dump_registry(cls, file=None):
"""Debug helper to print the ABC registry."""
print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
for name in sorted(cls.__dict__.keys()):
if name.startswith("_abc_"):
value = getattr(cls, name)
print("%s: %r" % (name, value), file=file)
def __instancecheck__(cls, instance):
"""Override for isinstance(instance, cls)."""
# Inline the cache checking
subclass = instance.__class__
if subclass in cls._abc_cache:
return True
subtype = type(instance)
if subtype is subclass:
if (cls._abc_negative_cache_version ==
ABCMeta._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
return False
# Fall back to the subclass check.
return cls.__subclasscheck__(subclass)
return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
def __subclasscheck__(cls, subclass):
"""Override for issubclass(subclass, cls)."""
# Check cache
if subclass in cls._abc_cache:
return True
# Check negative cache; may have to invalidate
if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
# Invalidate the negative cache
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
elif subclass in cls._abc_negative_cache:
return False
# Check the subclass hook
ok = cls.__subclasshook__(subclass)
if ok is not NotImplemented:
assert isinstance(ok, bool)
if ok:
cls._abc_cache.add(subclass)
else:
cls._abc_negative_cache.add(subclass)
return ok
# Check if it's a direct subclass
if cls in getattr(subclass, '__mro__', ()):
cls._abc_cache.add(subclass)
return True
# Check if it's a subclass of a registered class (recursive)
for rcls in cls._abc_registry:
if issubclass(subclass, rcls):
cls._abc_cache.add(subclass)
return True
# Check if it's a subclass of a subclass (recursive)
for scls in cls.__subclasses__():
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True
# No dice; update negative cache
cls._abc_negative_cache.add(subclass)
return False
在使用抽象基类的时候,其实上面抽象基类Iterable就是用了元类ABCMeta,ABCmeta中 其实是重写了__new__方法,在我们继承Iterable这个抽象基类有没有实现或者重写抽象基类中的所有抽象方法(abstractmethod ),可以在元类里面进行检查,如果不通过则会抛出未实现抽象基类当中的所有abstractmethod方法。因为继承抽象基类的类在实例化的过程中必须实现重写所有abstractmethod方法,如果不实现就应该抛出异常。元类可以控制类的实例化过程就是这样子的。
介绍一下
getattr()
def getattr(object, name, default=None): # known special case of getattr
"""
getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
When a default argument is given, it is returned when the attribute doesn't
exist; without it, an exception is raised in that case.
"""
元类简单实现orm
# 需求
import numbers
class Field:
pass
#为了判断字段的类型,需要使用属性描述符
class DataIntField(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 postive int")
if max_value is not None:
if not isinstance(min_value, numbers.Integral):
raise ValueError("max_value must be int")
elif min_value < 0:
raise ValueError("max_value must be postive int")
if min_value is not None and max_value is not None:
if min_value>max_value:
raise ValueError("min_value musy 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")
if value<self.min_value or value >self.max_value:
raise ValueError("value must be between min_value and max_value")
self._value=value
class DataCharField(Field):
def __init__(self,db_column,max_length=None):
self._value=None
self.db_column=db_column
self.max_length=max_length
if max_length is None:
raise ValueError("you must spcify max_length for charfield")
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError("str value need")
if len(value) > self.max_length:
raise ValueError("value len must be shorter than max_length")
self._value = value
# 元类
class ModelMetaClass(type):
# name类名 bases 继承的类 attr 类的属性
def __new__(cls, name,bases,attrs, **kwargs):
if name=="BaseModel":
return super().__new__(cls, 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,"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):
#这个父类不知道用户传递什么参数 *args **kwargs
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))
#拼凑字符串
# sql="insert user(name,age) value('xiaopang',23) " 应该是动态的
sql="insert {db_table}({fields}) value({values})"\
.format(db_table=self._meta['db_table'],fields=","\
.join(fields),values=",".join(values))
#这里没写具体插入数据库当中操作
pass
class User(BaseModel):
# def __init__(self):
# pass
name=DataCharField(db_column="name",max_length=10)
age=DataIntField(db_column="age",min_value=0,max_value=100)
class Meta:
db_table="user"
if __name__=="__main__":
user=User(name="xiaopang",age=23)
user.name="xiaopang"
user.age=age=23
user.save()
完结
下一篇 迭代器和生成器