python隨用隨學-元類

python中的一切都是對象

按着我的邏輯走:

  • 首先接受一個公理,python中的一切都是對象.不要問為什麼,吉大爺(Guido van Rossum,python之父)人當初就是這麼設計的,不服去找他評理!
  • 類是python語言的一個元素
  • 那麼類也是一個對象

是的,類也是一個對象,因為python中萬物皆對象. 只不過類這個對象具有創建對象的能力.

那麼問題來了,既然類也是一個對象,那本質上它和其他對象沒有啥區別,我們可以對它進行一下操作:

  • 可以賦值給一個變量
  • 可以拷貝
  • 可以增加屬性
  • 可以當做入參傳遞給函數

而且因為類也是一個對象,所以可以在程序運行的時候進行動態的傳遞.

def choose_class(name):      if name == 'foo':          class Foo:              pass            return Foo      else:          class Bar:              pass            return Bar      Myclass = choose_class('foo')    print(Myclass)  print(Myclass())  

看一個簡單的例子.首先choose_class()函數中動態的創建了類,然後將類作為返回值 然後我們通過一個變量Myclass接收了這個返回值.

看下面兩個print,第一個打印的是我們創建出的類. 第二個打印的是我們創建的類創建的對象.

<class '__main__.choose_class.<locals>.Foo'>  <__main__.choose_class.<locals>.Foo object at 0x0000017102113208>  

使用type創建類

class type(object) class type(name, bases, dict) With one argument, return the type of an object. The return value is a type object and generally the same object as returned by object.__class__. The isinstance() built-in function is recommended for testing the type of an object, because it takes subclasses into account.

type這個類一看就很特別,因為首字母是小寫的…它可以用來創建一個類…

Foo = type('Foo', (object,), {'bar':True})    print(Foo)    foo = Foo()    print(foo)  print(foo.bar)  

這裡的代碼和下面的類創建代碼是類似的

class Foo(object):         bar = True  

向類中添加方法

Foo = type('Foo', (object,), {'bar': True})      def get_bar(self):      print(self.bar)      FooChild = type('FooChild', (Foo,), {'get_bar': get_bar})  print(hasattr(FooChild,'get_bar'))  myfoo = FooChild()  print(myfoo)  myfoo.get_bar()  

元類

簡單來說,元類就是用來創建類的類. 比如我們上面使用的type就是一個python內置的元類.

比如我們來干一件神器的事情

__metaclass__屬性(python3不再支持)

NOTE!!! python3中不再支持__metaclass__屬性,而是在定義類的時候使用metaclass=XXXX參數

在寫一個類的時候,如果我們指定了__metaclass__屬性,就說明我們指明了這個類的元類.

比如我們定義一個類:

class MyClass():      pass  

類在定義的時候,它在內存中還沒有生成,知道它會被調用,python會做以下的事情:

  1. 在MyClass中尋找__metaclass__屬性,如果有,python會在內存中創建一個名字叫MyClass的類對象(注意,是類!對象)
  2. 如果在MyClass中沒有找到__metaclass__屬性,那麼會在它的父類中繼續尋找
  3. 如果父類里還沒有,那麼回到模塊底層去找
  4. 如果模塊底層還沒有,Python會使用type作為元類來創建這個類.

自定義元類

使用函數定義元類

def upper_attr(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)      class MyMetacalss(object, metaclass=upper_attr):      bar = 'bip'      print(hasattr(MyMetacalss, 'bar'))  print(hasattr(MyMetacalss, 'BAR'))    c = MyMetacalss()  print(c.BAR)    

使用class來定義元類

先看一下文檔中__new__方法的定義.

object.__new__(cls[, ...]) Called to create a new instance of class cls.__new__()is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of__new__()should be the new object instance (usually an instance of cls). Typical implementations create a new instance of the class by invoking the superclass』s __new__() method using super().__new__(cls[, ...]) with appropriate arguments and then modifying the newly-created instance as necessary before returning it. If __new__()returns an instance of cls, then the new instance』s __init__()method will be invoked like__init__(self[, ...]),where self is the new instance and the remaining arguments are the same as were passed to__new__(). If __new__() does not return an instance of cls, then the new instance』s __init__()method will not be invoked. __new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

明白了__new__方法,那改寫起來就簡單了

class Upper(type):      def __new__(cls, name, bases, attrs):          attr = ((name, value) for name, value in attrs.items() if not name.startswith('__'))          uppercase_attr = dict((name.upper(), value) for name, value in attr)          return type.__new__(cls, name, bases, uppercase_attr)  

還有一種寫法是通過調用父類的__new__()方法來返回類對象. 如果子類需要重寫__new__()方法的話,一般要調用父類的__new__()方法.

class Upper(type):      def __new__(cls, name, bases, attrs):          attr = ((name, value) for name, value in attrs.items() if not name.startswith('__'))          uppercase_attr = dict((name.upper(), value) for name, value in attr)          return super(Upper,cls).__new__(cls, name, bases , uppercase_attr)  

另外通過上面的文檔,我們也能知道一個對象創建的時候會有那些操作.

  1. 調用__new__()方法來創建內存空間
  2. 之後調用__init__()方法來初始化參數.

參考資料:

  1. python中的元類-簡書
  2. 深刻理解Python中的元類(metaclass)以及元類實現單例模式