python(七):元类与抽象基类
一、实例创建
在创建实例时,调用__new__方法和__init__方法,这两个方法在没有定义时,是自动调用了object来实现的。python3默认创建的类是继承了object。
class A(object):
def __init__(self, *args, **kwargs):
self.name, self.age, self.gender = args[:3]
def __new__(cls, *args, **kwargs):
print("__new__ has called.")
return super(A, cls).__new__(cls)
# 可写为 super().__new__(cls) 或 object.__new__(cls) a = A("Li", 27, "male")
print(a.name, a.age, a.gender) """
__new__ has called.
Li 27 male """
二、类的创建
以class关键字开头的上下文在定义时就已经被解释执行。而函数(包括匿名函数)在没被调用时是不执行的。这个过程本质上做了一件事情:从元类type那里创建了一个名为A的类,开辟类内存空间,并读取class语句的上下文,将类属性和方法写进去。
print("--解释器开始执行--")
def func():
print("what the hell?") print("--开始读取class关键字的上下文--")
class A:
name = "A"
func()
print("--上下文结束--") def fn1():
print("--开始读取匿名函数--")
def fn2():
pass
pass
print("--读取结束--")
print("--解释器执行结束--") """
--解释器开始执行--
--开始读取class关键字的上下文--
what the hell?
--上下文结束--
--解释器执行结束--
"""
" 使用class语句定义新类时,将会发生很多事情。首先,类主体将为作其自己的私有字典内的一系列语句来执行。其内容里语句的执行与正常代码中的执行过程相同,只是增加了会在私有成员(名称以__开头)上发生的名称变形。然后,类的名称、基类列表和字典将传递给元类的解构函数,以创建相应的类对象。最后,调用元类type(),这里可以自定义。在python3中,使用class Foo(metaclass=type)来显式地指定元类。如果没有找到任何__metaclass__值,Python将使用默认的元类type。" -- <<python 参考手册(第四版)>>
class_name = "Foo" # 类名
class_parents = (object, ) # 基类
# 类主体
class_body = """
name = "Foo"
def __init__(self, x):
self.x = x
def hello(self):
print("Hello")
"""
class_dict = {}
# 在局部字典class_dict中执行类主体
exec(class_body, globals(), class_dict)
# 创建类对象Foo
Foo = type(class_name, class_parents, class_dict) # type可以指定
Foo("X").hello()
# Hello
type类创建类时,指定了类的三个部分: class_name, class_parent, class_dict。这一步是在底层实现的。
string = """name = 'Li'
age = 2712
"""
# 字符串必须是换行符或分号分割
dic = {}
exec(string, globals()) # globals表示执行字符串后的结果保存到全局命名空间中
print(name, age)
print(dic)
exec(string, globals(), dic) # locals表示执行字符串后的结果保存到局部一个映射对象中
print(dic) """
Li 2712
{}
{'name': 'Li', 'age': 2712}
"""
exec函数用法
我们可以用type动态地创建类。你可以用上面的方式去实现类的上下文,也可以直接定义函数并给到字典里,尽管它看起来有些"污染"全局空间:
class_name = "A"
class_parent = () label = "hello world" def init(self, name, age):
self.name = name
self.age = age
def hello(self):
print("Hello, i'm %s, %s." % (self.name, self.age)) A = type(class_name, class_parent, {"__init__": init, "hello": hello, "label": label}) a = A("Li", 18)
a.hello()
print(a.label) """
Hello, i'm Li, 18.
hello world
"""
三、元类的实现过程
复制代码 print("First...")
class MyType(type):
print("MyType begin ...")
def __init__(self, *args, **kwargs):
print("Mytype __init__", self, *args, **kwargs , sep="\r\n", end="\r\n\r\n")
type.__init__(self, *args, **kwargs) # 调用type.__init__ def __call__(self, *args, **kwargs):
print("Mytype __call__", *args, **kwargs)
obj = self.__new__(self) # 第一个self是Foo,第二个self是F("Alex")
print("obj ",obj, *args, **kwargs)
print(self)
self.__init__(obj,*args, **kwargs)
return obj def __new__(cls, *args, **kwargs):
print("Mytype __new__", cls, *args, **kwargs, sep="\r\n", end="\r\n\r\n")
return type.__new__(cls, *args, **kwargs)
print("MyType end ...") print('Second...')
class Foo(metaclass=MyType):
print("begin...")
def __init__(self, name):
self.name = name
print("Foo __init__") def __new__(cls, *args, **kwargs):
print("Foo __new__", end="\r\n\r\n")
return object.__new__(cls)
print("over...") def __call__(self, *args, **kwargs):
print("Foo __call__", self, *args, **kwargs, end="\r\n\r\n") print("third...")
f = Foo("Alex")
print("f",f, end="\r\n\r\n")
f()
print("fname",f.name) """
First...
MyType begin ...
MyType end ...
Second...
begin...
over...
Mytype __new__
<class '__main__.MyType'>
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x10ad89268>, '__new__': <function Foo.__new__ at 0x10ad89488>, '__call__': <function Foo.__call__ at 0x10ad86ae8>} Mytype __init__
<class '__main__.Foo'>
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x10ad89268>, '__new__': <function Foo.__new__ at 0x10ad89488>, '__call__': <function Foo.__call__ at 0x10ad86ae8>} third...
Mytype __call__ Alex
Foo __new__ obj <__main__.Foo object at 0x10ae2ac88> Alex
<class '__main__.Foo'>
Foo __init__
f <__main__.Foo object at 0x10ae2ac88> Foo __call__ <__main__.Foo object at 0x10ae2ac88> fname Alex """
假设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__方法。
至此,一个实例就算创建完成了。
四、抽象基类
抽象基类有两个特点:
1.规定继承类必须具有抽象基类指定的方法
2.抽象基类无法实例化
基于上述两个特点,抽象基类主要用于接口设计
实现抽象基类可以使用内置的abc模块
import abc
class Human(metaclass=abc.ABCMeta):
@abc.abstractmethod # 规定子类必须有名为introduce的实例方法
def introduce(self):
pass @abc.abstractproperty # 规定子类必须有名为country的装饰器方法
def country(self):
pass @abc.abstractclassmethod # 规定子类必须有名为gender的类方法
def gender(cls):
pass
@abc.abstractstaticmethod # 规定子类必须有名为hello的静态方法
def hello():
pass
class Person(Human):
__country = "China"
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
return "I'm {}, {}.".format(self.name, self.age) @property
def country(self):
return Person.__country @classmethod
def gender(cls):
return "female" @staticmethod
def hello():
print("What the hell?") person = Person("Li", 24)
print(person.introduce())
print(person.country)
print(Person.gender())
person.hello() # I'm Li, 24.
# China
# female
# What the hell?
collections.abc模块收集了常用的抽象基类。感兴趣的话可以打开collections.abc查看源码。
__all__ = ["Awaitable", "Coroutine",
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
]
python(七):元类与抽象基类的更多相关文章
- python 用abc模块构建抽象基类Abstract Base Classes
见代码: #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/08/01 16:58 from abc import ABCMet ...
- 类,抽象基类,接口类三者间的区别与联系(C++)
结构上的区别: 普通类:数据+方法+实现 抽象类:数据+方法(一定包含虚方法n>=1)+部分方法的实现 接口类:方法(纯虚方法) http://www.cnblogs.com/Tris-wu/p ...
- 【Python】【元编程】【从协议到抽象基类】
"""class Vector2d: typecode = 'd' def __init__(self,x,y): self.__x = float(x) self.__ ...
- Python中的对象行为与特殊方法(二)类型检查与抽象基类
类型检查 创建类的实例时,该实例的类型为类本身: class Foo(object): pass f = Foo() 要测试实例是否属于某个类,可以使用type()内置函数: >>> ...
- Python中的抽象基类
1.说在前头 "抽象基类"这个词可能听着比较"深奥",其实"基类"就是"父类","抽象"就是&quo ...
- 流畅python学习笔记:第十一章:抽象基类
__getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...
- 流畅的python学习笔记:第十一章:抽象基类
__getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...
- 4.6 C++抽象基类和纯虚成员函数
参考:http://www.weixueyuan.net/view/6376.html 总结: 在C++中,可以通过抽象基类来实现公共接口 纯虚成员函数没有函数体,只有函数声明,在纯虚函数声明结尾加上 ...
- c++之——抽象基类
在一个虚函数的声明语句的分号前加上 =0:就可以将一个虚函数变成纯虚函数,其中,=0只能出现在类内部的虚函数声明语句处.纯虚函数只用声明,而不用定义,其存在就是为了提供接口,含有纯虚函数的类是抽象基类 ...
随机推荐
- 如何高效的使用 Git
-- 代码昨天还是运行好好的今天就不行了. 代码被删了. 突然出现了一个奇怪的 bug,但是没人知道怎么回事. 如果你出现过上面的任何一种情况,那本篇文章就是为你准备的. 除了知道 git add, ...
- mysql学习之旅
1,mysql安装脚本 #!/bin/bash RSYNC='10.39.2.120::wenhui/database/install' CMAKE='cmake-2.8.6.tar.gz' MYSQ ...
- 037——VUE中表单控件处理之表单修饰符:lazy/number/trim
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【2018 “百度之星”程序设计大赛 - 初赛(B)- 1001】degree
Problem Description 度度熊最近似乎在研究图论.给定一个有 N 个点 (vertex) 以及 M 条边 (edge) 的无向简单图 (undirected simple graph) ...
- 求小于等于k长度的最大区间和
题意 给出一个序列,求长度小于等于k的最大区间和并输出起点和终点 1<=n<=100000 1<=k<=n 题解:先算出前缀和,利用单调队列的性质,在单调队列中存储sum[ ...
- APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量
线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定 ...
- python_安装第三方库
1.有一个专门可下载安装第三方库的网址: http://www.lfd.uci.edu/~gohlke/pythonlibs/ Ctrl+f 搜索要下载的第三方库,并下载 2.库文件都是以 whl ...
- Tomcat 环境安装
本文以Tmcat 7版本在Windows Server 2012 64位系统下安装讲解,JAVA环境安装配置参见:http://www.cnblogs.com/fklin/p/6670760.html ...
- XML 测验
测试地址http://www.w3school.com.cn/quiz/quiz.asp?quiz=xml 1.XML指的是? 您的回答:eXtensible Markup Language 2.XM ...
- EasyPlayer RTSP安卓Android播放器架构简析
本文转自EasyDarwin开源团队成员John的博客:http://blog.csdn.net/jyt0551/article/details/73310641 EasyPlayer 是一款小而美的 ...