一、 type()

1、创建类的两种方式

方式一

class MyClass(object):
    def func(self,name):
        print(name)

myc = MyClass()

print(MyClass, type(MyClass))
print(myc, type(myc))

我们创建了一个名为MyClass的类,并实例化了这个类,得到其对象myc

上面代码打印的结果为:

<class '__main__.MyClass'>    <class 'type'>
<__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'>

 

type()函数可以查看一个类型或变量的类型,MyClass是一个class,它的类型就是type,而h是一个实例,它的类型就是class myc

我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出MyClass类,而无需通过Class MyClass(object)...的定义:

 

方式二

动态创建类

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

 

 

def fn(self, name='world'): # 先定义函数
    print('Hello, %s.' % name)

MyClass = type('MyClass', (object,), {'func':fn}) # 创建MyClass类,得到一个type的类对象
# MyClass = type('MyClass', (object,), {'func':lambda self,name:name}) # 创建MyClass类

myc=MyClass()

print(MyClass, type(MyClass))
print(myc, type(myc))

打印结果:

<class '__main__.MyClass'>   <class 'type'>
<__main__.MyClass object at 0x0364B830>   <class '__main__.MyClass'>

 

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名func上。

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

 

type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类(元类,默认为type,也可以自定制)创建而来。type也是由type创建。。

 

二、元类(metaclass)

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义元类(不自定义时,默认用type),然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是元类创建出来的“实例”。

 

使用metaclass的两种方式

class MyType(type):
    def __init__(self,*args,**kwargs):
       super(MyType,self).__init__(*args,**kwargs)

    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls,*args, **kwargs)
        cls.__init__(obj,*args, **kwargs)
        return obj

def with_metaclass(base):
    return MyType("MyType",(base,),{})

# 方式一
class Foo(metaclass=MyType):
    def __init__(self,name):
        print('Foo __init__')
        self.name = name


#方式二    在Flask的wtform的源码中用到过
# class Foo(with_metaclass(object)):
#     def __init__(self,name):
#         print('Foo __init__')
#         self.name = name


a=Foo('name')

 

 

三、 __init__,__new__,__call__三个特殊方法

__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)

      其必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。

__init__ : 对象的初始化, 是一个实例方法,第一个参数是self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。
__call__ : 对象可call,注意不是类,是对象。

1.对于__new__

class Bar(object):
    pass

class Foo(object):
    def __new__(cls, *args, **kwargs):
        return Bar()

print(Foo()) 

打印结果为:

<__main__.Bar object at 0x0090F930>

 可以看到,输出来是一个Bar对象。

  __new__方法在类定义中不是必须写的,如果没定义,默认会调用object.__new__去创建一个对象。如果定义了,就是会覆盖,使用自定义的,这样就可以自定制创建对象的行为。

 单例模式也可以通过这种方式来创建。点击查看【几种单例模式的创建方法】

2.对于__init__

class Person(object):

  def __init__(self, name, age):
    self.name = name
    self.age = age
    print('执行__init__')

  def __new__(cls, *args, **kwargs):
      obj = object.__new__(cls) # 创建对象
      print('执行__new__方法')
      return obj

p1 = Person('hc', 24)
print(p1)

打印结果:

执行__new__方法
执行__init__
<__main__.Person object at 0x028EB830>

 

  __init__ 方法通常用在初始化一个类实例的时候,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。从打印结果就可以看出来

  若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。

 

3.对于__call__

  对象通过提供__call__(slef, *args ,**kwargs)方法可以模拟函数的行为,如果一个对象x提供了该方法,就可以像函数一样使用它,也就是说x(arg1, arg2…) 等同于调用x.__call__(self, arg1, arg2) 。

class Foo(object): 
  def __call__(self): 
    pass 
 
f = Foo()    #类(),即执行元类的__call__
f()    #对象(),即执行Foo的__call__ 

 

 

 

 

4、实例化对象的完整过程

class Foo(Bar):
    pass

当我们写如这段代码时,Python做了如下的操作:

  Foo中有metaclass这个属性吗?如果是,Python会在内存中通过metaclass创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到metaclass,它会继续在Bar(父类)中寻找metaclass属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass,并尝试做同样的操作。如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。

  把上面这段话反复读几次,现在的问题就是,你可以在metaclass中放置些什么代码呢?

  答案就是:可以创建一个类的东西。

  那么什么可以用来创建一个类呢?

  type,或者任何使用到type或者子类化type的东东都可以。

 

以上面的代码为例,我们实例化一个对象obj=Foo()时,会先执行Foo类的__new__方法,没写时,用父类的__new__方法,创建一个对象,并返回,然后执行__init__方法(自己有就用自己的,没有就用父类的),对创建的对象进行初始化。

obj()会执行Foo类的__call__方法,没有则用父类的

我们现在已经知道,类也是对象,是元类的对象,即我们实例化一个类时,调用其元类的__call__方法。

 

元类处理过程:定义一个类时,使用声明或者默认的元类对该类进行创建,对元类求type运算,得到父元类(该类声明的元类的父元类),调用父元类的__call__函数,在父元类的__call__函数中, 调用该类声明的元类的__new__函数来创建对象(该函数需要返回一个对象(指类)实例),然后再调用该元类的__init__初始化该对象(此处对象是指类,因为是元类创建的对象),最终返回该类

 

1.对象是类创建,创建对象时候类的__init__方法自动执行,对象()执行类的 __call__ 方法
2.类是type创建,创建类时候type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)

版权声明:本文为huchong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/huchong/p/8260151.html