python面向对象

 面向对象思想

  面向过程: 侧重在过程. 事物的发展流程

优点: 编码和入门简单.

缺点: 可扩展性差.

  面向对象: 侧重在对象. 万事万物皆为对象.

优点: 可扩展性强(多态)

缺点: 上手难. 写起来麻烦

  类与对象的关系

类是对事物的总结. 抽象的概念. 类⽤来描述对象. 对象是类的实例化的结果. 对象能执⾏哪些⽅法. 都由类来决定. 

类中定义了什么.对象就拥有什么。

  总结,在python中支持面向对象和面向过程. 使用哪一个你自己决定.python面向对象,一切皆为对象

面向对象三大特征

封装

隐藏对象的属性和实现细节,仅对外提供公共访问方式。

封装原则

1. 将不需要对外提供的内容都隐藏起来

2. 把属性都隐藏,提供公共方法对其访问

实现

私有变量和私有方法,

  在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

  类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式

  不是真正的私有化,根于类名变形后,在类外面也可以访问到

封装与扩展性

  只对外提供接口,修改内部的代码不影响外部调用者的代码

  封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口,只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

  继承

子类继承父类. 子类会自动拥有父类中除了私有内容外的其他所有内容, python是多继承.

优点,代码复用。

继承与抽象

  先抽象再继承,抽象即抽取类似或者说比较像的部分

继承原理

  如何实现继承,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序表。

  属性查找方法

    1. 子类会先于父类被检查
    2. 多个父类会根据他们在列表中的顺序被检查
    3. 如果下一个类存在两个合法的选择,优先选择第一个父类
多继承

经典类与新式类

  在py2.2版本之前,只有经典类:经典类在基类的根如果什么都不写,表示继承XX

  在py2.2版本之后,出现了新式类:新式类的特点是基类根是object

  在py3版本中使用的都是新式类,默认继承object

MRO

  py2.2之前,经典类,深度优先

  py2.2,经典类,深度优先;新式类,列表去重,后因单调性问题,之后版本采用c3算法。

  py2.3-py2.7,经典类,深度优先;新式类,C3算法

  py3,C3算法,全部为新式类,默认继承object类(如果继承关系中没有菱形继承深度优先就够了)

C3算法

  C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。

  本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。

  单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。

  基本上按照深度优先,但把多各类产生的共同继承的类最后去找。

  可以通过类名.__mro__ 查看

  算法

    先拆分。 拆到你能看出结果为止. 反着进行merge()运算

    合并 - 归并。merge(元组, 元组, 元组,...)

      头和尾在比对,如果下一个尾没有这个头, 头就出现. 如果头在后面的尾出现.跳过该元组. 继续下一个头. 直到最后一个元组. 根自己匹配

 1 class A:    pass
 2 class B(A):    pass
 3 class C(A):    pass
 4 class D(B, C):    pass
 5 class E(C, A):    pass
 6 class F(D, E):    pass
 7 class G(E):    pass
 8 class H(G, F):    pass
 9 
10 print(H.__mro__)
11 
12 # H的MRO
13 # H的MRO: ??
14 #
15 # L(H)= H + L(G) + L(F) -> H + (GECA) + (FDBECA) -> HGFDBECA (MRO)
16 
17 # L(G) = G + L(E) -> G +(ECA) -> GECA
18 # L(F) = F + L(D) + L(E) -> F +(DBCA) + (ECA) -> FDBECA
19 
20 # L(E) = E +  L(C) + L(A) -> E + (CA) + A -> ECA
21 # L(D) = D + L(B)+ L(C)  -> D + (BA) + (CA) -> DBCA
22 
23 # L(c) = C + A  CA
24 # L(B) = B + A  BA
25 
26 # L(A) = A
27 
28 #
29 # + merge((B,), (A, ), 元组......) 算法
MRO

 

super()

super可以访问MRO列表中的下一个类中的内容. 找父类

1 class Person(object):
2     def __init__(self, name):
3         self.name = name
4 
5 
6 class Student(Person):
7     def __init__(self, name, age):
8         super(Student, self).__init__(name)
9         self.age = age
super
 1 class Person(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5 
 6 class Student(Person):
 7     def __init__(self, name, age):
 8         super(Student, self).__init__(name)
 9         self.age = age  # MRO + super
10 
11         class Init(object):
12             def __init__(self, v):
13                 print("init")
14                 self.val = v
15 
16         class Add2(Init):
17             def __init__(self, val):
18                 print("Add2")
19                 super(Add2, self).__init__(val)
20                 print(self.val)
21                 self.val += 2
22 
23         class Mult(Init):
24             def __init__(self, val):
25                 print("Mult")
26                 super(Mult, self).__init__(val)
27                 self.val *= 5
28 
29         class HaHa(Init):
30             def __init__(self, val):
31                 print("哈哈")
32                 super(HaHa, self).__init__(val)
33                 self.val /= 5
34 
35         class Pro(Add2, Mult, HaHa): pass
36 
37         class Incr(Pro):
38             def __init__(self, val):
39                 super(Incr, self).__init__(val)
40                 self.val += 1
41             # Incr Pro Add2 Mult HaHa Init
42 
43 # p = Incr(5)
44 # print(p.val)
45 
46 # c = Add2(2)
47 # print(c.val)
48 
49 # 结论: 不不管super()写在哪⼉儿. 在哪⼉儿执⾏行行. ⼀一定先找到MRO列列表. 根据 MRO列列表的顺序往下找. 否则⼀一切都是错的
50 # super(Incr, self).__init__(val)
51 # 在self的RMO表中Incr下一个类
MRO + super

 

组合

组合是指,在一个类中以另外一个类的对象作为数据类型,称为类的组合

  1,继承的方式,是一种‘是’的关系,比如,人是动物,白马是马。

  2,组合的方式,是一种‘有’的关系,比如,教授有生日,教授有学生。

  当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。

接口,抽象类

接口

  接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。归一化。

  实现,模仿java中的interface

    1,第三方模块

    2,使用继承

      1,继承基类的方法,并且做出自己的改变或者扩展(代码重用)

      2,声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能

 1 class Interface(object):
 2     def read(self):
 3         pass
 4 
 5     def write(self):
 6         pass
 7 
 8 
 9 class Text(Interface):
10     def read(self):
11         print('文本读取数据的方法')
12 
13     def write(self):
14         print('文本写入数据的方法')
15 
16 
17 class Sata(Interface):
18     def read(self):
19         print('硬盘读取数据的方法')
20 
21     def write(self):
22         print('硬盘写入数据的方法')
23 # 上面代码看起来像接口,但根本没有起到接口的作用,子类可以不实现接口方法。
接口

 

抽象类

  只能被继承,不能被实现。子类必须实现。借助模块实现。

  抽象类是对类的抽象,抽取相同的内容而来。

 1 import abc
 2 
 3 
 4 class All_file(metaclass=abc.ABCMeta):
 5     all_type = 'file'
 6 
 7     @abc.abstractmethod
 8     def read(self):
 9         pass
10 
11     @abc.abstractmethod
12     def write(self):
13         # print(1)
14         pass
15 
16 
17 class Txt(All_file):
18     def read(self):
19         print('文本读取数据的方法')
20 
21     def write(self):
22         print('文本写入数据的方法')
23 
24 
25 class Sata(All_file):
26     def read(self):
27         print('硬盘读取数据的方法')
28 
29     def write(self):
30         print('硬盘写入数据的方法')
31 
32 
33 if __name__ == '__main__':
34     Txt().read()
35     Sata().read()
抽象类  

  多态

一类事物有多种形态。

python天生支持多态,python没有多态的具体表现形式。

动物有多种形态:人,狗,猪

 1 import abc
 2 
 3 
 4 class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
 5     @abc.abstractmethod
 6     def talk(self):
 7         pass
 8 
 9 
10 class People(Animal):  # 动物的形态之一:人
11     def talk(self):
12         print('say hello')
13 
14 
15 class Dog(Animal):  # 动物的形态之二:狗
16     def talk(self):
17         print('say wangwang')
18 
19 
20 class Pig(Animal):  # 动物的形态之三:猪
21     def talk(self):
22         print('say aoao')
多态

 

鸭子类型

  当我看到一只鸟像鸭子一样游来游去,像鸭子一样游来游去,像鸭子一样呱呱叫,我就把那只鸟叫做鸭子。

  我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

  不崇尚根据继承所得来的相似

  如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型

    list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的

  优点:松耦合 每个相似的类之间都没有影响

  缺点:太随意了,只能靠自觉

类的成员

变量

1. 实例变量(字段)
  给对象用的。
2. 类变量(静态变量)
  类变量,多个对象共享的,最好用类名访问,规范。

方法

1. 成员(实例)方法 加了self的.调用的时候。必须用对象去访问. (重点)
2. 类方法,类方法是将类本身作为对象进行操作的方法。类方法使用@classmethod装饰器定义,其第一个参数是类,约定写为cls。类对象和实例都可以调用类方法
  使用场景:当这个方法的操作只涉及静态属性的时候 就应该使用classmethod来装饰这个方法

1 class Classmethod_Demo():
2     role = 'dog'
3 
4     @classmethod
5     def func(cls):
6         print(cls.role)
classmethod

3. 静态方法,静态方法是一种普通函数,就位于类定义的命名空间中,它不会对任何实例类型进行操作。使用装饰器@staticmethod定义静态方法。类对象和实例都可以调用静态方法
  使用场景:如果一个函数既和对象没有关系也和类没有关系那么就用staticmethod将这个函数变成一个静态方法

1 class Staticmethod_Demo():
2     role = 'dog'
3 
4     @staticmethod
5     def func():
6         print("当普通方法用")
7 
8 
9 Staticmethod_Demo.func()
staticmethod

 

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。

属性

用方法来描述我们的属性信息.像访问变量一样访问方法。
注意:
  1. @propery 改变一个方法成为属性
  2. 这个方法只能有一个参数, self
  3. 必须有返回值.

 1 # property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
 2 import math
 3 
 4 
 5 class Circle:
 6     def __init__(self, radius):  # 圆的半径radius
 7         self.radius = radius
 8 
 9     @property
10     def area(self):
11         return math.pi * self.radius ** 2  # 计算面积
12 
13     @property
14     def perimeter(self):
15         return 2 * math.pi * self.radius  # 计算周长
16 
17 
18 c = Circle(10)
19 print(c.radius)
20 print(c.area)  # 可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
21 print(c.perimeter)  # 同上
22 # c.area = 1       #不能给property方法赋值
property属性

 

私有

1. 私有变量,__变量
2. 私有方法,__方法
注意:私有化的只能由本类访问,对象不能访问,子类无法继承。
另外,python中是伪私有,可以用 obj._classname.__attrName 访问到。

 1 # 类变量,类方法,成员方法,属性,静态方法
 2 import time
 3 
 4 
 5 class Person:
 6     nationality = 'China'  # 类变量
 7 
 8     def __init__(self, name=None, age=18):
 9         self.name = name
10         self.age = age
11 
12     def eat(self):  # 成员方法
13         self.__class__.nationality = 'Germany'  # 在成员方法内访问类变量
14         print(self.__class__.nationality)
15         print('in eat')
16 
17     @classmethod  # 类方法
18     def walk(cls):
19         cls.nationality = 'USA'  # 在类方法中访问类变量
20         print(cls.nationality)
21         print('in walk')
22 
23     @staticmethod  # 静态方法,与类无关,只是在类里面而已
24     def sleep():
25         print('in sleep')
26 
27     @property  # 属性
28     def birthday(self):
29         return int(time.strftime('%Y')) - self.age
类的成员示例代码 

类与类的关系

 世间万事的关系

1. 依赖关系,我用的到你,但你不属于我。
2. 关联关系,两种事物必须是互相关联的. 但是在某些特殊情况下是可以更改和更换的。
3. 组合关系,属于关联关系中的一种特例. 侧重点是xxx和xxx聚合成xxx. 各⾃有各⾃的生命周期.
4. 聚合关系,属于关联关系中的⼀种特例. 写法上差不多. 组合关系比聚合还要紧密.
5. 继承关系
6. 实现关系

python内部的主要关系

1. 依赖关系,在方法中传递参数.
2. 组合关系,self.xxx = xxxx
3. 继承关系,self,默认的self都是访问这个方法的对象.

类的特殊成员

类名()会自动执行 __init__()
对象()会自动执行 __call__()
对象[key] 会⾃动执⾏__getitem__()
对象[key] = value 会⾃动执⾏__setitem__()
del 对象[key] 会自动执⾏ __delitem__()
对象+对象 会⾃动执⾏ __add__()
with 对象 as 变量 会⾃动执⾏__enter__ 和__exit__
打印对象的时候 会自动执⾏ __str__
干掉可哈希 __hash__ == None 对象就不可哈希了了.

类名.__mro__ # 类的MRO(方法解析顺序)
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类
类名.__bases__# 类所有父类构成的元组
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
类名.__del__ # 析构方法,当对象在内存中被释放时,自动触发执行。
类名.__iter__ # 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
类名.__new__ 类名.__metaclass__ # 创建并返回一个对象

创建对象的真正步骤

⾸先, 在执⾏类名()的时候. 系统会⾃动先执行__new__()来开辟内存. 此时新开辟出来的内存区域是空的. 紧随其后, 系统自动调⽤__init__()来完成对象的初始化⼯工作. 按照时间轴来算.
1. 加载类
2. 开辟内存(__new__)
3. 初始化(__init__)
4. 使⽤用对象⼲干xxxxxxxxx

反射

 isinstance, type, issubclass

type()可以帮我们判断xxx是否是xxx数据类型的
isinstance()也可以判断xxx是yyy类型的数据. 还可以判断该对象是否是xxx家族体系中的(只能往上判断)
issubclass()这个内置函数可以帮我们判断XX类是否是YY类的子类

区分函数和方法

1. 类外面,都是定义的函数一定是函数
2. 类里面
  类⽅法. 不论任何情况, 都是⽅法
  静态⽅法, 不论任何情况. 都是函数
  实例方法, 如果是实例访问. 就是方法. 如果是类名访问就是函数。 对象.方法 方法 类名.方法 函数
用程序判断
  # 用 FunctionType MethodType 判断
  from types import MethodType, FunctionType
  isinstance()
其实只要是与类绑定的都是方法,反之都是函数。

反射

仅限于内存层面,不会影响源代码
1. hasattr(obj, str) 判断对象中是否包含xxxx(str)
2. getattr(obj, str) 从对象中获取xxx(str)
3. setattr(obj, str, value) 把对象中的str设置成value
4. delattr(obj, str) 从对象中删除xxxx(str)

异常处理

异常(在程序运行中出现的错误),异常发生后,异常之后的代码不再执行。

异常处理,提高程序健壮性,出现异常,程序不会崩溃。

捕获异常

 1 # 捕获异常
 2 try:
 3     a = [1, 2]
 4     print(a[2])
 5 except IndexError as e:
 6     print(e)
 7 except KeyError as e:
 8     print(e)
 9 except Exception as e:
10     print(e)
11 else:
12     print('try内代码块没有异常则执行我')
13 finally:
14     print('无论异常与否,都会执行该模块,通常是进行清理工作')
15 # 一般 try except 就够用了,最后再加个 finally 做收尾工
捕获异常

 

 1 # finally,else 与return
 2 def f():
 3     try:
 4         a = [1, 2]
 5         # print(a[2])
 6         print('in try')
 7         # return 'return_try'
 8     except IndexError as e:
 9         print(e)
10         print('in IndexError')
11         return 'return_IndexError'
12     else:
13         print('in else')
14         return 'return_else'
15     finally:
16         print('无论异常与否,都会执行该模块,通常是进行清理工作')
17         print('in finally')
18         return 'return_finally'
19 
20 
21 print(f())
22 # 总结
23 # “如果try中没有异常,那么except部分将跳过,执行else中的语句。(前提是try里没有返回值)
24 # finally是无论是否有异常,最后都要做的一些事情。”(无论try里是否有返回值)
25 # 这里补充一句,在含有return的情况下,并不会阻碍finally的执行。(但是会阻碍else)
26 # 不要在try else里写返回值。如果没有finally,就写在最后,或者只写在finally里。
27 # try except else里都是做某事   而不是处理返回
大坑 finally,else 与return

 

主动抛出

1 # 主动触发异常
2 try:
3     raise TypeError('类型错误')
4 except Exception as e:
5     print(e)

 

自定义异常

写个新类继承Exception,就OK

1 # 自定义异常
2 class MyException(Exception):
3     pass
4 
5 
6 try:
7     raise MyException('哈哈哈,异常了')
8 except MyException as e:
9     print(e)
 1 import traceback
 2 
 3 try:
 4     a = [12, 2]
 5     print(a[3])
 6 
 7 except IndexError as e:
 8     val = traceback.format_exc()  # 获取到堆栈信息
 9     print(e)
10     print(val)
11 finally:
12     print(233)
显示详细异常信息

断言

断言是用来检查非法情况而不是错误情况的,用来帮开发者快速定位问题的位置。
对一个函数而言,一般情况下,断言用于检查函数输入的合法性,要求输入满足一定的条件才能继续执行;
在函数执行过程中出现的异常情况使用异常来捕获

1 assert 1 == 1
2 assert 1 == 2
3 v1 = 1
4 v2 = 2
5 assert (v1 > v2), '{0} is not bigger than {1}'.format(v1, v2)

 

类的约束

约束是对子类进行的约束。

在python中有两种解决方案

1. 提取父类,然后在父类中定义好方法,在这个方法中什么都不用干,就抛一个异常(NotImplementError)就可以了,这样所有子类继承都必须重写次方法,否则报错。(推荐,python风格)

 1 class Login:
 2     def login(self):
 3         raise NotImplementedError('没有实现login方法')
 4 
 5 
 6 class Normal(Login):
 7     # 普通人登录
 8     def login(self):
 9         pass
10 
11 
12 class Member(Login):
13     # 会员登录
14     def denglu(self):
15         pass
16 
17 
18 class Admin(Login):
19     # 管理员登录
20     def login(self):
21         pass
22 
23 
24 # 项目经理的总入口
25 def login(obj):
26     print('准备验证码')
27     obj.login()
28     print('进入主页')
29 
30 
31 if __name__ == '__main__':
32     login(Normal())
33     login(Member())  # 报错        NotImplementedError: 没有实现login方法
34     login(Admin())
提取父类

2. 使用元类来描述父类,在元类中给出一个抽象方法,这样子类就不得不实现此方法。

 1 from abc import ABCMeta, abstractmethod
 2 
 3 
 4 class Login(metaclass=ABCMeta):
 5     @abstractmethod
 6     def login(self):
 7         pass
 8 
 9 
10 class Normal(Login):
11     # 普通人登录
12     def login(self):
13         pass
14 
15 
16 class Member(Login):
17     # 会员登录
18     def denglu(self):
19         pass
20 
21 
22 class Admin(Login):
23     # 管理员登录
24     def login(self):
25         pass
26 
27 
28 # 项目经理的总入口
29 def login(obj):
30     print('准备验证码')
31     obj.login()
32     print('进入主页')
33 
34 
35 if __name__ == '__main__':
36     login(Normal())
37     login(Member())  # 报错    TypeError: Can't instantiate abstract class Member with abstract methods login
38     login(Admin())
元类描述

总结: 约束,其实就是父类对子类进行约束.子类必须要写xxx⽅法.在python中约束的方式和方法有两种:
  1. 使用抽象类和抽象方法, 由于该⽅案来源是java和c#.所以使用频率还是很少的
  2. 使用人为抛出异常的方案,并且尽量抛出的是NotImplementError. 这样比较专业, 而且错误比较明确.(推荐)

单例模式

单例模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

更多方式实现 http://funhacks.net/2017/01/17/singleton/

使用 __new__ 实现

 1 class Singleton:
 2     def __new__(cls, *args, **kwargs):
 3         if not hasattr(cls, '_instance'):
 4             cls._instance = object.__new__(cls)
 5         return cls._instance
 6 
 7 
 8 one = Singleton()
 9 two = Singleton()
10 # one和two完全相同,可以用id(), ==, is检测
11 print(id(one))
12 # 29097904
13 print(id(two))
14 # 29097904
15 print(one == two)
16 # True
17 print(one is two)

元类

参考

https://www.cnblogs.com/wupeiqi/p/4766801.html

https://www.cnblogs.com/kuaizifeng/p/9082181.html

阅读代码

1 class Foo: pass
2 obj = Foo  # obj是通过Foo十实例化的对象

 

上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类 本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

1 print(type(obj))  # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
2 print(type(Foo))  # 输出:<type 'type'>              表示,Foo类对象由 type 类创建

Python中的类的创建方式有两种

1,普通方式(通过class)

1 class Foo: pass
2 obj = Foo  # obj是通过Foo十实例化的对象

 

2,特殊方式(type创建)

1 def func(self):
2     print(233)
3 Foo = type('Foo', (object,), {'func': func})
4 Foo().func()  # 233
5 # type第一个参数:类名
6 # type第二个参数:当前类的基类
7 # type第三个参数:类的成员

元类的创建

 1 print("First...")
 2 
 3 
 4 class MyType(type):
 5     print("MyType begin ...")
 6 
 7     def __init__(self, *args, **kwargs):
 8         print("Mytype __init__", self, *args, **kwargs, sep="\r\n", end="\r\n\r\n")
 9         type.__init__(self, *args, **kwargs)  # 调用type.__init__
10 
11     def __call__(self, *args, **kwargs):
12         print("Mytype __call__", *args, **kwargs)
13         obj = self.__new__(self)  # 第一个self是Foo,第二个self是F("Alex")
14         print("obj ", obj, *args, **kwargs)
15         print(self)
16         self.__init__(obj, *args, **kwargs)
17         return obj
18 
19     def __new__(cls, *args, **kwargs):
20         print("Mytype __new__", cls, *args, **kwargs, sep="\r\n", end="\r\n\r\n")
21         return type.__new__(cls, *args, **kwargs)
22 
23     print("MyType end ...")
24 
25 
26 print('Second...')
27 
28 
29 class Foo(metaclass=MyType):
30     print("begin...")
31 
32     def __init__(self, name):
33         self.name = name
34         print("Foo __init__")
35 
36     def __new__(cls, *args, **kwargs):
37         print("Foo __new__", end="\r\n\r\n")
38         return object.__new__(cls)
39 
40     print("over...")
41 
42     def __call__(self, *args, **kwargs):
43         print("Foo __call__", self, *args, **kwargs, end="\r\n\r\n")
44 
45 
46 print("third...")
47 f = Foo("Alex")
48 print("f", f, end="\r\n\r\n")
49 f()
50 print("fname", f.name)
51 
52 """
53 First...
54 MyType begin ...
55 MyType end ...
56 Second...
57 begin...
58 over...
59 Mytype __new__
60 <class '__main__.MyType'>
61 Foo
62 ()
63 {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x10ad89268>, '__new__': <function Foo.__new__ at 0x10ad89488>, '__call__': <function Foo.__call__ at 0x10ad86ae8>}
64 
65 Mytype __init__
66 <class '__main__.Foo'>
67 Foo
68 ()
69 {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x10ad89268>, '__new__': <function Foo.__new__ at 0x10ad89488>, '__call__': <function Foo.__call__ at 0x10ad86ae8>}
70 
71 third...
72 Mytype __call__ Alex
73 Foo __new__
74 
75 obj  <__main__.Foo object at 0x10ae2ac88> Alex
76 <class '__main__.Foo'>
77 Foo __init__
78 f <__main__.Foo object at 0x10ae2ac88>
79 
80 Foo __call__ <__main__.Foo object at 0x10ae2ac88>
81 
82 fname Alex
83 
84 """
元类的创建

解释一波 

  假设MyType是type类,type有三个特殊方法__init__、__call__、__new__。
  首先, First请忽略掉吧。假设底层就这样搞了一个type类,它的名字叫MyType。
  其次,Second这一步。解释器发现class和Foo(),会知道要从元类MyType中"实例化"一个类对象。
  它会首先扫描class Foo()的整个上下文,并分成三部分,类名、基类元组,和私有字典。
  然后它会告诉解释器,马上调用MyType(就是Type)类来创建一个名为Foo的类,来开辟内存空间,把这个Foo的私有字典(包括属性和方法)给放进去。
  于是解释器执行了MyType.__new__,并继续执行MyType.__init__。来创建一个名为Foo的类对象。
  再次,Third这一步。
  首先通过Foo()来调用MyType.__call__,来实例化一个Foo类。它相当于Foo = Type()
  然后依次执行Foo.__new__和Foo.__init__,来实例化一个实例对象。
  Foo()相当于: MyType()(),而MyType()就是F。于是,在a = Foo(),实际上执行了MyType()()。前面说过,实例+()会调用所属类的__call__方法,同样地,类 + ()会调用类所属元类(MyType)的  __call__方法。
  至此,一个实例就算创建完成了。

图示

 

posted @ 2018-10-10 21:40  写bug的日子  阅读(223)  评论(0编辑  收藏  举报