日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python可以实现什么黑科技_Python黑科技之元类

發布時間:2024/9/19 python 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python可以实现什么黑科技_Python黑科技之元类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Python中的類

在理解元類之前,你需要了解Python中的類。Python中的類借鑒自Smalltalk。

在大多數編程語言中,類只是描述對象生成方式的一段代碼,在Python里面看起來也是這樣。比如下面的代碼

>>> class ObjectCreator(object):pass

...

>>> my_object = ObjectCreator()

>>> print(my_object)

<__main__.ObjectCreator object at 0x1008e4a90>

但在Python中,類也是對象。是的,類是對象

class關鍵字聲明了一個類,Python會執行class這一段代碼,生成一個對象,下面的操作在內存中創建一個對象,取名為"ObjectCreator"。

>>> class ObjectCreator(object): pass

這個類可以創建自己的對象,這也是類的功能。

但是它本身也是一個對象,因此

可以將它賦值給一個變量

可以復制

可以往里邊添加屬性

也可以將其作為參數傳入一個函數

舉個例子

>>> class ObjectCreator(object): pass

...

>>> print(ObjectCreator)

>>> def echo(o): print(o)

...

>>> echo(ObjectCreator)

>>> ObjectCreator.new_attribute = 'foo'

>>> print(hasattr(ObjectCreator, 'new_attribute'))

True

>>> print(ObjectCreator.new_attribute)

foo

>>> ObjectCreatorMirror = ObjectCreator

>>> print(ObjectCreatorMirror.new_attribute)

foo

>>> id(ObjectCreatorMirror)

140433925632016

>>> id(ObjectCreator)

140433925632016

>>> print(ObjectCreatorMirror())

<__main__.ObjectCreator object at 0x1072342d0>

動態生成類

就像對象一樣,類也可以動態生成,因為它本身就是對象。

可以在函數中用class來創建一個類

>>> def choose_class(name):

... if name == 'foo':

... class Foo(object):

... pass

... return Foo

... else:

... class Bar(object):

... pass

... return Bar

...

>>> MyClass = choose_class('foo')

>>> print(MyClass)

>>> print(MyClass())

<__main__.Foo object at 0x107234290>

上面的函數這也并不是那么智能,因為還是要完成得定義一個類。

既然類也是對象,那么一定有辦法可以生成類。

當使用class關鍵字時,Python會自動創建類。和其他特性一樣,Python也提供了手動創建類的方式。

還記得type這個函數嗎?這個函數可以讓你知道一個對象的類型:

>>> print(type(1))

>>> print(type("1"))

>>> print(type(ObjectCreator))

>>> print(type(ObjectCreator()))

這個函數還有另外的功能,就是動態創建類,它通過傳入類的描述作為參數來做到這一點。

(同一個函數根據不同的參數有完全不同的兩個共同,這看起來確實有點奇怪。這是Python為了向后兼容而引入的一個問題)。

可以這樣使用type

type(name of the class,

tuple of the parent class (for inheritance, can be empty),

dictionary containing attributes names and values)

舉個例子

>>> class MyShinyClass(object): pass

可以這樣被創建

>>> MyShinyClass = type('MyShinyClass', (), {})

>>> print(MyShinyClass)

>>> print(MyShinyClass())

<__main__.MyShinyClass object at 0x109250ad0>

>>>

可以看到,類的名稱被當作是參數傳給了type。type通過字典來定義類的屬性,比如

>>> class Foo(object): bar = True

等同于

>>> Foo = type('Foo', (), {'bar': True})

通過type定義的類可以像用class定義的類一樣使用

>>> print(Foo)

>>> print(Foo.bar)

True

>>> f = Foo()

>>> print(f)

<__main__.Foo object at 0x109250b50>

>>> print(f.bar)

True

>>>

當然也可以編寫子類類繼承它

>>> class FooChild(Foo): pass

等同于

>>> FooChild = type('FooChild', (Foo,), {})

>>> print(FooChild)

>>> print(FooChild.bar)

True

如果想給類添加方法,只需要定義一個函數,并且為類添加這個屬性即可

>>> def echo_bar(self): print(self.bar)

...

>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})

>>> hasattr(Foo, 'echo_bar')

False

>>> hasattr(FooChild, 'echo_bar')

True

>>> my_foo = FooChild()

>>> my_foo.echo_bar()

True

在動態創建類之后,也可以添加方法,效果和在創建的時候添加方法一樣。

>>> def echo_bar_more(self): print('yet another method')

...

>>> FooChild.echo_bar_more = echo_bar_more

>>> hasattr(FooChild, 'echo_bar_more')

True

可以看到Python中的類也是對象,可以隨時,動態地創建類。

在使用class關鍵字后,Python也是通過這樣的方法,使用元類創建類的。

元類

一般定義一個類,是為了創建對象,對吧?

但是我們已經知道在Python中類也是對象。

元類就是類的類,它用來創建類。大概像下面這樣

MyClass = MetaClass()

MyObject = MyClass()

之前講過,可以這樣用type

MyClass = type('MyClass', (), {})

可以這樣用,是因為type函數實際上是一個元類,Python就是用type來創建類。

也許你會問,那為什么type不寫成Type呢?

我只能猜測這是為了和str,int這樣能創建對象的關鍵詞保持一致,所以首字母用了小寫。

通過查看__class__屬性,也能看出一些端倪。

Python中萬物皆是對象,這其中包括了整形,字符串,函數和類。它們都是通過一個類創建的。

>>> age = 35

>>> age.__class__

>>> name = 'bob'

>>> name.__class__

>>> def foo(): pass

...

>>> foo.__class__

>>> class Bar(object): pass

...

>>> b = Bar()

>>> b.__class__

那么__class__的__class__是什么呢?

>>> age.__class__.__class__

>>> name.__class__.__class__

>>> foo.__class__.__class__

>>> b.__class__.__class__

>>> type.__class__

所以元類就是類的類,它創建的對象是類。

也可以叫它“工廠類”。

type是Python內置的元類,但是你也可以創建自己的元類。

__metaclass__屬性

在創建一個類的時候可以加上__metaclass__屬性,比如下面這樣

class Foo(object):

__metaclass__ = something ...

[...]

如果這樣寫,Python會用自定義的元類來創建Foo。

小心,這樣可能會帶來風險。

在寫class Foo(object)的時候,Foo這個類并沒有在內存中創建這個類的實例。

Python會在類的定義中尋找__metaclass__這個屬性,如果找到了,就用這個元類創建Foo,如果找不到,就用type來創建這個類。

所以在下面的代碼中

class Foo(Bar): pass

Python會執行下面的邏輯

Foo中有__metaclass__這個屬性嗎?

如果有,就用__metaclass__定義的元類來創建Foo這個類;

如果找不到__metaclass__這個屬性,Python會在模塊中尋找__metaclass__,如果找到了,就用它來創建Foo這個類;

如果還是找不到,Python會用Bar的元類(應該是type)來創建Foo這個類。

注意,子類不會繼承__metaclass__這個屬性,但是會繼承父類的元類。就是說,如果Bar使用__metaclass__這個屬性來創建Bar這個類,子類不會繼承這個行為。

現在問題來了,__metaclass__里面的內容可以是什么呢?

答案是:可以創建類的內容

什么可以創建一個類呢?type,type的子類,或者用到了type的類

自定義元類

元類的主要作用是在創建類的時候改變這個類。

根據當前的上下文創建類,這個特性可以用來開發API。

舉個簡單例子,現在你想要模塊中所有的類中的屬性都是大寫開頭的。有很多種方式來實現這一點,現在我們通過使用修改模版中的__metaclass__屬性來做到這一點。

這樣,這個模塊中所有的類都會用自定義的元類來創建,我們只需要在元類中將類中的所有屬性首字母改成大寫。

幸運的是,__metaclass__是可以被調用的,所以不必是“類”。

下面來看看例子吧

def upper_attr(future_class_name, future_class_parents, future_class_attr):

uppercase_attr = {}

for name, val in future_class_attr.items():

if not name.startswith('__'):

uppercase_attr[name.upper()] = val

else:

uppercase_attr[name] = val

return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr

class Foo():

bar = 'bip'

print(hasattr(Foo, 'bar'))

# 輸出: False

print(hasattr(Foo, 'BAR'))

# 輸出: True

f = Foo()

print(f.BAR)

# 輸出:bip

現在我們用類來實現一個元類

class UpperAttrMetaclass(type):

def __new__(upperattr_metaclass, future_class_name,

future_class_parents, future_class_attr):

uppercase_attr = {}

for name, val in future_class_attr.items():

if not name.startswith('__'):

uppercase_attr[name.upper()] = val

else:

uppercase_attr[name] = val

return type(future_class_name, future_class_parents, uppercase_attr)

但是上面的方法沒有用到type類中的方法,我們可以通過調用type中的__new__方法來實現

class UpperAttrMetaclass(type):

def __new__(upperattr_metaclass, future_class_name,

future_class_parents, future_class_attr):

uppercase_attr = {}

for name, val in future_class_attr.items():

if not name.startswith('__'):

uppercase_attr[name.upper()] = val

else:

uppercase_attr[name] = val

return type.__new__(upperattr_metaclass, future_class_name,

future_class_parents, uppercase_attr)

可能你注意到了上面代碼中的upperattr_metaclass參數,這沒有什么特別的,__new__方法總是會將定義它的類作為第一個參數傳入,這就和self一樣,上面的例子中,把

upperattr_metaclass打印出來,可以看到類似的結果。

當然,這里的取名只是為了說清明這些變量,但就和self一樣,這些參數都有固定的取名,比如下面這樣

class UpperAttrMetaclass(type):

def __new__(cls, clsname, bases, dct):

uppercase_attr = {}

for name, val in dct.items():

if not name.startswith('__'):

uppercase_attr[name.upper()] = val

else:

uppercase_attr[name] = val

return type.__new__(cls, clsname, bases, uppercase_attr)

為了讓UpperAttrMetaclass繼承自type這一特性表現的更清楚,可以使用super

class UpperAttrMetaclass(type):

def __new__(cls, clsname, bases, dct):

uppercase_attr = {}

for name, val in dct.items():

if not name.startswith('__'):

uppercase_attr[name.upper()] = val

else:

uppercase_attr[name] = val

return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

代碼中使用元類,可以實現一些黑科技。而元類本身只實現下面的功能。

中斷類的創建

修改類

返回修改后的類

元類實現的取舍

既然__metaclass__可以是任意可調用的對象,為什么要用類而不是函數來實現它呢?

主要考慮到下面幾個原因

語義上更清晰。

面向對象。元類可以繼承自元類,元類甚至可以使用元類。

代碼結構更清晰。

可以根據不同想法,在__new__,__init__,__call__不同的函數中實現不同的功能

既然都叫元類了,那肯定就是類嘛!

為什么要使用元類

現在還有一個問題,為什么要使用這樣一個令人費解的功能呢?

當然,一般情況下,這個功能不會被用到

99%的人都不會用到元類,如果你還在想是否要用它,那么你就不需要用到它(真正有需求用它的人不會問這個問題) Python Guru Tim Peters

元類的主要功能是用來開發API。一個典型的例子就是Django ORM,它可以這樣定義一個model

class Person(models.Model):

name = models.CharField(max_length=30)

age = models.IntegerField()

但是下面的代碼卻不會返回一個IntegerField的對象,而是返回一個int,甚至可以從數據庫中去取這個值。

guy = Person(name='bob', age='35')

print(guy.age)

之所以能這樣實現,是因為model.Model中定義了__metaclass__,通過定義的元類將Person類轉換成一條SQL語句。

Django通過元類將代碼改寫,這樣就可以只暴露簡單的API,而實現復雜的功能。

結語

類可以用來創建對象。

而事實上,類本身也是對象,元類的對象。

>>> class Foo(object): pass

>>> id(Foo)

>>> 140257261595760

Python中萬物都是對象,它們要么是類的實例,要么是元類的實例。

除了type。

type是它自己的元類,Python在實現層面,做了一些工作來實現這一點。

元類是很復雜的。比較簡單的場景不一定要用到它,要改變一個類,可以通過下面兩種技術實現

如果需要改變類的行為,99%情況下應該使用上面的兩種方法。

但是99%情況下,根本就不需要改變類的行為

“本譯文僅供個人研習、欣賞語言之用,謝絕任何轉載及用于任何商業用途。本譯文所涉法律后果均由本人承擔。本人同意簡書平臺在接獲有關著作權人的通知后,刪除文章?!?/p>

總結

以上是生活随笔為你收集整理的python可以实现什么黑科技_Python黑科技之元类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。