一:類也是對象
類就是一組用來描述如何生成一個對象的代碼。
類也是一個對象瘩蚪,只要你使用關(guān)鍵字 class,python 解釋器在執(zhí)行的時候就會創(chuàng)建一個對象。
下面這段代碼:
class ObjectCreator(object):
pass
會在內(nèi)存中創(chuàng)建一個對象惧眠,名字就是 ObjectCreator,這個對象(類)自身擁有創(chuàng)建對象(類實例)的能力于个。
類的本質(zhì)是一個對象氛魁,你可以拷貝它,可以將它賦給一個變量厅篓,將它作為參數(shù)傳遞給函數(shù)秀存,可以為它增加屬性。
以下為實例:
class ObjectCreator(object):
pass
print(ObjectCreator) #打印一個類
def echo(data):
print(data)
echo(ObjectCreator) #將類作為參數(shù)傳遞給函數(shù)
ObjectCreator.newAttribute = 'new attr' #為類添加新屬性
print(hasattr(ObjectCreator,'newAttribute'))
print(ObjectCreator.newAttribute)
ObjectCreatorCopy = ObjectCreator #將類復(fù)制給一個變量
print(ObjectCreatorCopy)
print(ObjectCreatorCopy())
'''
輸出:
<class '__main__.ObjectCreator'>
<class '__main__.ObjectCreator'>
True
new attr
<class '__main__.ObjectCreator'>
<__main__.ObjectCreator object at 0x0000000001EA9860>
'''
二:動態(tài)的創(chuàng)建類
1 通過 return class 動態(tài)的構(gòu)建需要的類
類也是對象羽氮,所以我們再運行的時候動態(tài)的創(chuàng)建它們或链。在函數(shù)體中創(chuàng)建類:
def create_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo
else:
class Berry(object):
pass
return Berry
my_class = create_class('foo')
print(my_class)#返回類
print(my_class())#返回類的實例
'''輸出:
<class '__main__.create_class.<locals>.Foo'>
<__main__.create_class.<locals>
2 通過 type 函數(shù)構(gòu)造類
上述方法需要你自己編寫整個類的代碼。由于類也是對象档押,所以也必須是通過什么東西來生成才對澳盐。
內(nèi)置函數(shù) type 可以讓知道一個對象的類型是什么,like:
class Demo(object):
pass
print(type(100))
print(type('str'))
print(type(Demo))
print(type(Demo()))
'''
輸出:
<class 'int'>
<class 'str'>
<class 'type'>
<class '__main__.Demo'>
'''
另外令宿,type 還可以動態(tài)的創(chuàng)建類叼耙。type 可以接收一個類的描述作為參數(shù),然后返回一個類粒没。
type 的語法:
type(類名筛婉,父類的元組(針對繼承的情況,可以為空)癞松, 包含屬性的字典(名稱和值))
比如下面的代碼:
class MyclassDemo(object):
pass
#可以手動通過type創(chuàng)建
MyclassDemo = type('MyclassDemo',(),{}) #返回一個類對象
print(MyclassDemo)
print(MyclassDemo())
'''
輸出:
<class '__main__.MyclassDemo'>
<__main__.MyclassDemo object at 0x000000000213A3C8>
'''
下面通過一個例子看看 type 是如何創(chuàng)建類的:
#1 構(gòu)建Foo類
class Foo(object):
bar = True
# 使用type構(gòu)建Foo
Foo = type('Foo', (), {'bar': True})
print(Foo)
# 2繼承Foo類
class FooChild(Foo):
pass
# 使用type構(gòu)建FooChild
FooChild = type('Foochild',(Foo,), {})
print(FooChild)
print(FooChild.bar)
# 3為FooChild類增加方法
def echo_bar(self):
print(self.bar)
FooChild = type('FooChild',(Foo,),{'echo_bar': echo_bar})
print(hasattr(Foo,'echo_bar'))
print(hasattr(FooChild, 'echo_bar'))
my_foo = FooChild()
my_foo.echo_bar()
'''
輸出:
<class '__main__.Foo'>
<class '__main__.Foochild'>
True
False
True
True
'''
可以看到爽撒,在 python 中冕碟,類也是對象,你可以動態(tài)的創(chuàng)建類匆浙。這就是當我們使用關(guān)鍵字 class 時 python 在幕后做的事安寺,而這就是通過元類來實現(xiàn)的。
三:元類
1 什么是元類首尼?
python 中類也是對象挑庶,元類就是用來創(chuàng)建這些類(對象)的,元類是類的類软能,你可以這樣理解:
MyClass = MetaClass() #元類創(chuàng)建類
MyObject = MyClass() #類創(chuàng)建對象
#MyClass通過type()來創(chuàng)建出MyClass類迎捺,它就是type()類的一個實例
函數(shù) type 實際上是一個元類,type 就是 python 在背后創(chuàng)建所有類的元類〔榕牛現(xiàn)在想知道 type 為什么會全部采用小寫形式而不是 Type 呢凳枝?好吧,我猜這是為了和 str 保持一致性跋核,str 是用來創(chuàng)建字符串對象的類岖瑰,int 是用來創(chuàng)建整數(shù)對象的類。type 就是創(chuàng)建類對象的類砂代。
你可以通過檢查class屬性來看到這一點蹋订。python 中所有的東西 都是對象。這包括整數(shù)刻伊,字符串露戒,函數(shù)以及類。它們?nèi)际菍ο蟠废洌沂菑囊粋€類創(chuàng)建而來智什。
age = 33
print(age.__class__)
name = 'alice'
print(name.__class__)
def foo():
pass
print(foo.__class__)
class Bar(object):
pass
b = Bar()
print(b.__class__)
# 那么任何一個__class__的__class__屬性又是什么呢?丁屎?
print(age.__class__.__class__)
print(foo.__class__.__class__)
print(b.__class__.__class__)
'''
輸出:
<class 'int'>
<class 'str'>
<class 'function'>
<class '__main__.Bar'>
<class 'type'>
<class 'type'>
<class 'type'>
'''
因此荠锭,元類就是創(chuàng)建類這種對象的東西,type 就是 python 的內(nèi)建元類悦屏,當然了节沦,你也可以創(chuàng)建自己的元類。
2 __metaclass__
屬性
你可以在寫一個類的時候為其添加__metaclass__
屬性础爬,定義了__metaclass__
就定義了這個類的元類。
class Foo(object): #py2
__metaclass__ = something
class Foo(metaclass=something): #py3
__metaclass__ = something
例如:當我們在編寫如下代碼時:
class Foo(Bar):
pass
在該類定義的時候吼鳞,它在內(nèi)存中還沒有生成看蚜,直到它被調(diào)用,python 做了如下的操作:
1) Foo 中有__metaclass__
這個屬性嗎赔桌?如果有供炎,python 會在內(nèi)存中通過__metaclass__
創(chuàng)建一個名字為 Foo 的類對象渴逻。
2) 如果沒有__metaclass__
這個屬性,它會繼續(xù)在父類中尋找__metaclass__
屬性音诫,并嘗試做和前面同樣的操作惨奕。
3)如果 python 在任何父類中都找不到__metaclass__
,它就會在模塊層次中去尋找__metaclass__
竭钝,并嘗試做同樣的事情
4)如果還是找不到__metaclass__
梨撞,python 就會用內(nèi)置的 type 來創(chuàng)建這個類對象。
現(xiàn)在的問題就是香罐,你可以在__metaclass__
中放置些什么代碼呢卧波?
答案就是:可以創(chuàng)建一個類的東西。那么什么可以用來創(chuàng)建一個類呢庇茫?type港粱,或者任何使用到 type 或者子類化 type 的東西都可以。
三 自定義元類
元類的主要目的就是為了在創(chuàng)建類時能夠自動的改變類旦签。通常查坪,你會為 API 做這樣的事,你希望可以通過創(chuàng)建符合當前上下文的類宁炫。
假想一個很傻的例子咪惠,你決定在你的模塊里所有類的屬性都應(yīng)該是大寫形式。有好幾種方法可以辦到淋淀,但其中一種就是通過設(shè)定__metaclass__
遥昧。采用這種方法,這個模塊里所有類都會通過這個元類來創(chuàng)建朵纷,我們只需要告訴元類把所有的屬性都改成大寫形式就萬事大吉了炭臭。
__metaclass__
實際上可以被任意調(diào)用,它并不需要是一個正式的類袍辞。所以鞋仍,我們這里就先以一個簡單的函數(shù)作為例子開始。
1 使用函數(shù)當做元類
#元類會自動將你通常傳給type的參數(shù)作為自己的參數(shù)傳入
def upper_attr(future_class_name,future_class_parents,future_class_attr):
'''返回一個類對象搅吁,將屬性都轉(zhuǎn)為大寫形式'''
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(),value) for name,value in attrs)
return type(future_class_name,future_class_parents,uppercase_attr)
class Foo(metaclass=upper_attr):
__metaclass__ = upper_attr
bar = 'bip'
print(hasattr(Foo,'bar'))
print(hasattr(Foo,'BAR'))
print(Foo.BAR)
'''
輸出:
False
True
bip
'''
2 使用 class 來當做元類
__metaclass__
必須返回一個類
'''
請記住威创,type實際上是一個類,就像str和int一樣谎懦,所以肚豺,你可以從type繼承
__new__ 是在__init__之前被調(diào)用的特殊方法。 __new__是用來創(chuàng)建對象并返回之的方法界拦,__new__()是一個類方法
而__init__只是用來將傳入的參數(shù)初始化給對象吸申,它是在類創(chuàng)建之后執(zhí)行的方法。
你很少用到__new__,除非你希望能夠控制對象的創(chuàng)建截碴。這里創(chuàng)建的對象是類梳侨,我們希望能夠自定義它,所以我們這里改寫__new__
如果你希望的話日丹,你也可以在__init__中做些事情走哺。還有一些高級的用法會涉及到改寫__call__特殊方法。但是我們這里不用哲虾,下面我們可以單獨討論這個使用
'''
class UpperAttrMetaClass(type):
def __new__(uppersttr_metaclass, future_class_name, future_class_parents, future_class_attr):
attrs = ((name,value) for name,value in future_class_attr.items()if not name.startswith('__'))
uppercase_attr = dict((name.upper(),value) for name, value in attrs)
return type(future_class_name,future_class_parents,uppercase_attr)
但是丙躏,這種方式其實不是 OOP。我們直接調(diào)用了 type妒牙,而且我們沒有改寫父類的new方法”撕撸現(xiàn)在讓我們這樣去處理:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 復(fù)用type.__new__方法
# 這就是基本的OOP編程,沒什么魔法湘今。由于type是元類也就是類敢朱,因此它本身也是通過__new__方法生成其實例,只不過這個實例是一個類.
return type.__new__(upperattr_metaclass,future_class_name,future_class_parents,uppercase_attr)
你可能已經(jīng)注意到了有個額外的參數(shù) upperattr_metaclass摩瞎,這并沒有什么特別的拴签。類方法的第一個參數(shù)總是表示當前的實例,就像在普通的類方法中的 self 參數(shù)一樣旗们。當然了蚓哩,為了清晰起見,這里的名字我起的比較長上渴。但是就像 self 一樣岸梨,所有的參數(shù)都有它們的傳統(tǒng)名稱。因此稠氮,在真實的產(chǎn)品代碼中一個元類應(yīng)該是像這樣的:
class UpperAttrMetaClass(type):
def __new__(cls, name,bases,dct):
attrs = ((name,value) for name,value in dct.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(),value)for name, value in attrs)
return type.__new__(cls,name,bases,uppercase_attr)
如果使用 super 方法的話曹阔,我們還可以使它變得更清晰一些。
class UpperAttrMetaClass(type):
def __new__(cls, name,bases,dct):
attrs = ((name,value) for name,value in dct.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(),value)for name, value in attrs)
return super(UpperAttrMetaClass,cls).__new__(cls,name,bases,uppercase_attr)
四:使用原來創(chuàng)建 ORM 的實例
我們通過創(chuàng)建一個類似 Django 中的 ORM 來熟悉一下元類的使用隔披,通常元類用來創(chuàng)建 API 是非常好得選擇帽芽,使用元類編寫很復(fù)雜澄耍,但是使用者可以非常簡潔的調(diào)用 API
class User(Model):
#定義類的屬性到列的映射:
id = IntegerField('id')
name = StringField('name')
email = StringField('email')
password = StringField('password')
例如:
#創(chuàng)建一個實例:
u = User(id=12345, name='Alice', email='test@orm.org', password='my-pwd')
#保存到數(shù)據(jù)庫
u.save()
接下來我們來實現(xiàn)這么個功能:
#coding:utf-8
# 一:首先來定義Field類誓琼,它負責(zé)保存數(shù)據(jù)庫的字段名和字段類型:
class Field(object):
def __init__(self,name,column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' %(self.__class__.__name__,self.name)
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField, self).__init__(name,'bigint')
# 二 定義元類讶迁,控制Model對象的創(chuàng)建
class ModelMetaClass(type):
#定義元類
def __new__(cls, name, bases, attrs):
if name == 'Model':
return super(ModelMetaClass, cls).__new__(cls,name,bases,attrs)
mappings = dict()
for k,v in attrs.items():
if isinstance(v,Field):
#保存類屬性和列的映射關(guān)系到mappings字典
print('found mappings:%s==>%s' %(k, v))
mappings[k] = v
'''# 保存類屬性和列的映射關(guān)系到mappings字典
print('found mappings:%s==>%s' % (k, v))
mappings[k] = v'''
for k in mappings.keys():
#將類屬性移除,使定義的類字段不污染User類屬性鬓长,只在實例中可以訪問這些key
attrs.pop(k)
attrs['__table__'] = name.lower()#假設(shè)表名和類名的小寫一樣谒拴,創(chuàng)建類時添加一個__table__類屬性
attrs['__mappings__'] = mappings #保存屬性和列的映射關(guān)系,創(chuàng)建類時添加一個__mappings__屬性
return super(ModelMetaClass, cls).__new__(cls,name,bases,attrs)
# 三 編寫Model類
class Model(dict,metaclass=ModelMetaClass):
__metaclass__ = ModelMetaClass
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute'%s'" %key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self,k,None))
sql = 'insert into %s (%s) values (%s)' %(self.__table__,','.join(fields), ','.join(params))
print('SQL: %s' %sql)
print('SRGS: %s' % str(args))
# 最后痢士,我們使用定義好的ORM接口彪薛,使用起來非常的簡單茂装。
class User(Model):
# 定義類的屬性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 創(chuàng)建一個實例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到數(shù)據(jù)庫:
u.save()
'''
輸出:
found mappings:id==><IntegerField:id>
found mappings:name==><StringField:username>
found mappings:email==><StringField:email>
found mappings:password==><StringField:password>
SQL: insert into user (id,username,email,password) values (?,?,?,?)
SRGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
'''
五:使用new 方法和元類方式分別實現(xiàn)單例模式
1怠蹂、new善延、init、call的介紹
new方法負責(zé)創(chuàng)建一個實例對象城侧,在對象被創(chuàng)建的時候調(diào)用該方法它是一個類方法易遣。new方法返回一個實例之后,會自動的調(diào)用init方法嫌佑,對實例進行初始化豆茫。如果new方法不返回值,或者返回的不是實例屋摇,那么它就不會自動的去調(diào)用init方法揩魂。
init方法負責(zé)將該實例對象進行初始化,在對象被創(chuàng)建之后調(diào)用該方法炮温,在new方法創(chuàng)建出一個實例之后對實例屬性進行初始化火脉。init方法可以沒有返回值。
call方法其實和類的創(chuàng)建過程和實例化沒有多大關(guān)系了柒啤,定義了call方法才能以函數(shù)的方式被執(zhí)行倦挂。例如:****
class A(object):
def __call__(self):
print('__call__ be called')
a = A()
a()
#輸出為:__call__ be called
#coding:utf-8
class Foo(object):
def __new__(cls, *args, **kwargs):
#__new__是一個類方法,在對象創(chuàng)建的時候調(diào)用
print("excute __new__")
return super(Foo,cls).__new__(cls,*args,**kwargs)
def __init__(self):
#__init__是一個實例方法担巩,在對象創(chuàng)建后調(diào)用方援,對實例屬性做初始化
print("excute __init")
f1 = Foo()
'''
輸出:
excute __new__
excute __init
'''
#可以看出new方法在init方法之前執(zhí)行
子類如果重寫new方法,一般依然要調(diào)用父類的new方法
class Child(Foo):
def __new__(cls, *args, **kwargs):
return super(Child, cls).__new__(cls, *args, **kwargs)
必須注意的是涛癌,類的new方法之后犯戏,必須生成本類的實例才能自動調(diào)用本類的init方法進行初始化,否則不會自動調(diào)用init.
class Foo(object):
def __init__(self, *args, **kwargs):
print('Foo __init__')
def __new__(cls, *args, **kwargs):
return object.__new__(Stranger, *args, **kwargs)
class Stranger(object):
def __init__(self,name):
print("class Stranger's __init__ be called" )
self.name = name
foo = Foo('test')
print(type(foo))
print(foo.name)
'''
輸出:
<class '__main__.Stranger'>
Traceback (most recent call last):
File "E:/py/awesome-python3-webapp/test/singleClass_demo.py", line 38, in <module>
print(foo.name)
AttributeError: 'Stranger' object has no attribute 'name'
'''
# 說明:如果new方法返回的不是本類的實例拳话,那么本類(Foo)的init和
# 生成的類(Stranger)的init都不會被調(diào)用
2 實現(xiàn)單例模式:
依照 python 官方文檔的說法先匪,new方法主要是當你繼承一些不可變的 class 時(比如 int,str假颇,tuple)時胚鸯,提供給你一個自定義這些類的實例化過程的途徑。還有就是實現(xiàn)自定義的 metaclass笨鸡。接下來我們分別通過這兩種方式來實現(xiàn)單例模式姜钳。
簡單來說,單例模式的原理就是通過在類屬性中添加一個判定為 ins_flag形耗,通過這個 flag 判斷是否已經(jīng)被實例化過了哥桥,如果被實例化過了就返回該實例。
1)new方法實現(xiàn)單例實例
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls,"_instance"):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)
# True
因為重寫new方法激涤,所以繼承至 Singleton 的類拟糕,在不重寫new的情況下都將是單例模式判呕。
2)元類實現(xiàn)單例
class Singleton(type):
def __init__(self, *args, **kwargs):
print('__init__')
self.__instance = None
super(Singleton,self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print('__call__')
if self.__instance is None:
self.__instance = super(Singleton,self).__call__(*args, **kwargs)
return self.__instance
class Foo(metaclass=Singleton):
__metaclass__ = Singleton #在代碼執(zhí)行到這里的時候,元類中的__new__方法和__init__方法其實已經(jīng)被執(zhí)行了送滞,而不是在Foo實例化的時候執(zhí)行侠草。且僅會執(zhí)行一次。
foo1 = Foo()
foo2 = Foo()
print(Foo.__dict__)#'_Singleton__instance': <__main__.Foo object at 0x0000000002238B00>
#存在一個私有屬性來保存屬性犁嗅,而不會污染Foo類(其實還是會污染边涕,只是無法直接通過__instance屬性訪問)
print(foo1 is foo2) ''' 輸出: __init__ __call__ __call__ {'__module__': '__main__', '__metaclass__': <class '__main__.Singleton'>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, '_Singleton__instance': <__main__.Foo object at 0x0000000002238B00>} True '''
基于這個例子:
- 我們知道元類 (Singleton) 生成的實例是一個類 (Foo), 而這里我們僅僅需要對這個實例(Foo) 增加一個屬性 (__instance) 來判斷和保存生成的單例。想想也知道為一個類添加一個屬性當然是在init中實現(xiàn)了褂微。
- 關(guān)于call方法的調(diào)用功蜓,因為 Foo 是 Singleton 的一個實例。所以 Foo() 這樣的方式就調(diào)用了 Singleton 的call方法宠蚂。不明白就回頭看看上一節(jié)中的call方法介紹式撼。
假如我們通過元類的new方法來也可以實現(xiàn),但顯然沒有通過init來實現(xiàn)優(yōu)雅求厕,因為我們不會為了為實例增加一個屬性而重寫new方法著隆。所以這個形式不推薦。
class Singleton(type):
def __new__(cls, name, bases, attrs):
print('__new__')
attrs["_instance"] = None
return super(Singleton, cls).__new__(cls, name, bases, attrs)
def __call__(self, *args, **kwargs):
print('__call__')
if self._instance is None:
self._instance = super(Singleton, self).__call__(*args, **kwargs)
return self._instance
class Foo(metaclass=Singleton):
__metaclass__ = Singleton
foo1 = Foo()
foo2 = Foo()
print(Foo.__dict__)
print(foo1 is foo2)
'''
輸出:
__new__
__call__
__call__
{'__module__': '__main__', '__metaclass__': <class '__main__.Singleton'>, '_instance': <__main__.Foo object at 0x00000000021F8C88>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
True
'''
python 類和元類 (metaclass) 的理解和簡單運用
深刻理解 Python 中的元類 (metaclass)
轉(zhuǎn)載于: https://www.cnblogs.com/1zhangwenjing/p/7750620.html