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()
完結
下一篇 迭代器和生成器