ToB企服应用市场:ToB评测及商务社交产业平台

标题: Python中类创建和实例化过程 [打印本页]

作者: 西河刘卡车医    时间: 2024-5-27 16:17
标题: Python中类创建和实例化过程
一、 type()

1、创建类的两种方式

方式一
  1. class MyClass(object):
  2.     def func(self,name):
  3. <class '__main__.MyClass'>    <class 'type'>
  4. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> print(name)
  5. myc = MyClass()
  6. print(MyClass, type(MyClass))
  7. print(myc, type(myc))
复制代码
我们创建了一个名为MyClass的类,并实例化了这个类,得到其对象myc
上面代码打印的结果为:
  1. <class '__main__.MyClass'>    <class 'type'>
  2. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'>
复制代码
type()函数可以查看一个类型或变量的类型,MyClass是一个class,它的类型就是type,而myc是一个实例,它的类型就是class MyClass。
我们说class的界说是运行时动态创建的,而创建class的方法就是使用type()函数。
type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出MyClass类,而无需通过Class MyClass(object)...的界说:
方式二
动态创建类
type(类名, 父类的元组(针对继续的环境,可以为空),包罗属性的字典(名称和值))
  1. def fn(self, name='world'): # 先定义函数
  2.     print('Hello, %s.' % name)
  3. MyClass = type('MyClass', (object,), {'func':fn}) # 创建MyClass类,得到一个type的类对象
  4. # MyClass = type('MyClass', (object,), {'func':lambda self,name:name}) # 创建MyClass类
  5. myc=MyClass()
  6. print(MyClass, type(MyClass))
  7. print(myc, type(myc))
复制代码
打印结果:
  1. <class '__main__.MyClass'>   <class 'type'>
  2. <__main__.MyClass object at 0x0364B830>   <class '__main__.MyClass'>
复制代码
要创建一个class对象,type()函数依次传入3个参数:
通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class界说时,仅仅是扫描一下class界说的语法,然后调用type()函数创建出class。
type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包罗整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类(元类,默认为type,也可以自定制)创建而来。type也是由type创建。。
二、元类(metaclass)

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass,直译为元类,简单的解释就是:
当我们界说了类以后,就可以根据这个类创建出实例,所以:先界说类,然后创建实例。
但是假如我们想创建出类呢?那就必须根据metaclass创建出类,所以:先界说元类(不自界说时,默认用type),然后创建类。
毗连起来就是:先界说metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类当作是元类创建出来的“实例”。
默认环境下,类是使用type()构造的。类主体在一个新的名称空间中执行,类名在本地绑定到类型的结果(名称、基、名称空间)。
可以通过在类界说行中传递元类关键字参数来定制类创建过程,或者从包罗此类参数的现有类继续。在下面的示例中,MyClass和MySubclass都是Meta的实例:
  1. class Meta(type):
  2.     pass
  3. class MyClass(metaclass=Meta):
  4.     pass
  5. class MySubclass(MyClass):
  6.     pass
复制代码
使用metaclass的两种方式
  1. class MyType(type):  # 自界说一个type的派生类    def __init__(self,*args,**kwargs):    print('xx')<class '__main__.MyClass'>    <class 'type'>
  2. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'>super(MyType,self).__init__(*args,**kwargs)    def __call__(cls, *args, **kwargs):<class '__main__.MyClass'>    <class 'type'>
  3. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> obj = cls.__new__(cls,*args, **kwargs)<class '__main__.MyClass'>    <class 'type'>
  4. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> cls.__init__(obj,*args, **kwargs)<class '__main__.MyClass'>    <class 'type'>
  5. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> return objdef with_metaclass(base):    return MyType("MyType2",(base,),{})# 方式一class Foo(metaclass=MyType):  # metaclass=MyType,即指定了由MyType创建Foo类,当程序运行,用到class Foo时,即调用MyType的__init__方法,创建Foo类    def __init__(self,name):<class '__main__.MyClass'>    <class 'type'>
  6. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> self.name = name#方式二    在Flask的wtform的源码中用到过# class Foo(with_metaclass(object)):#     def __init__(self,name):#<class '__main__.MyClass'>    <class 'type'>
  7. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'>  self.name = namea=Foo('name')
复制代码
方式一:即用类的形式
执行代码后,当遇到class Foo时即声明要创建一个Foo类,就会调用type的__init__方法创建类,由于此处(metaclass=MyType),即指定了Foo类的创建方式,所以会执行type的派生类MyType的__init__方法,创建Foo类,打印一次'xx'
方式二:用函数的形式
构建一个函数,返回一个type的派生类对象,例如叫type的派生类, 需要3个参数:name, bases, attrs
metaclass 原理

1.基础
metaclass的原理实在是这样的:当界说好类之后,创建类的时间实在是调用了type的__new__方法为这个类分配内存空间,创建好了之后再调用type的__init__方法初始化(做一些赋值等)。所以metaclass的所有magic实在就在于这个__new__方法里面了。
说说这个方法:__new__(cls, name, bases, attrs)
所以在创建类的过程,我们可以在这个函数里面修改name,bases,attrs的值来自由的到达我们的功能。这里常用的配合方法是
  1. getattr和setattr(just an advice)
复制代码
2.查找顺序
元类是由以下优先规则决定的:
假如“元类”存在,它就被使用了。
否则,假如至少有一个基类,则使用它的元类(这首先查找类属性,假如没有找到,则使用它的类型)。
否则,假如一个名为元类的全局变量存在,就会使用它。
三、 __init__,__new__,__call__三个特殊方法

1.对于__new__
  1. class Bar(object):    passclass Foo(object):    def __new__(cls, *args, **kwargs):<class '__main__.MyClass'>    <class 'type'>
  2. <__main__.MyClass object at 0x0288F8F0>   <class '__main__.MyClass'> return Bar()print(Foo())
复制代码
打印结果为:
  1. [/code]可以看到,输出来是一个Bar对象。
  2. __new__方法在类界说中不是必须写的,假如没界说,默认会调用object.__new__去创建一个对象。假如界说了,就是会覆盖,使用自界说的,这样就可以自定制创建对象的行为。
  3. [size=3]2.对于__init__[/size]
  4. [code]class Person(object):
  5.   def __init__(self, name, age):
  6.     self.name = name
  7.     self.age = age
  8.     print('执行__init__')
  9.   def __new__(cls, *args, **kwargs):
  10.       obj = object.__new__(cls) # 创建对象
  11.       print('执行__new__方法')
  12.       return obj
  13. p1 = Person('hc', 24)
  14. print(p1)
复制代码
打印结果:
  1. 执行__new__方法
  2. 执行__init__
  3. <__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) 。
  1. class Foo(object):
  2.   def __call__(self):
  3.     pass
  4. #学习中遇到问题没人解答?小编创建了一个Python学习交流群:153708845
  5. f = Foo()    #类(),即执行元类的__call__
  6. f()    #对象(),即执行Foo的__call__
复制代码
4、实例化对象的完备过程
  1. class Foo(Bar):
  2.     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__方法)
原始type的__call__应该是参数结构应该是:
  1. metaname, clsname, baseclasses, attrs
复制代码
原始type的__new__
  1. metaname, clsname, baseclasses, attrs
复制代码
原始type的__init__
  1. class_obj, clsname, baseclasses, attrs
复制代码
元类的__new__和__init__影响的是创建类对象的行为,父元类的__call__控制对子元类的 __new__,__init__的调用,就是说控制类对象的创建和初始化。父元类的__new__和__init__由更上层的控制,
一般来说,原始type是最初的父元类,其__new__和__init__是具有广泛意义的,即应该是分配内存、初始化相关信息等
元类__call__影响的是创建类的实例对象的行为,此时假如类自界说了__new__和__init__就可以控制类的对象实例的创建和初始化
__new__和__init__ 影响的是创建对象的行为,当这些函数在元类中时,影响创建的是类;同理,当这俩个函数在普通类中时,影响创建的是普通的对象实例。
__call__ 影响()调用行为, __call__是在创建类的时间调用,即: class Test(object): __metaclass__=type, 界说类时就是创建类,此时会调用元类的__call__,假如元类有继续,子元类界说时执行的是父元类的__call__。
假如是普通类实例化对象,调用的是普通类的__call__

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4