【转】Python-面向对象进阶
一、isinstance(obj, cls) and issubclass(sub, super)
1. isinstance(obj, cls),检查obj是否是类cls的对象
class A:
pass obj = A()
print(isinstance(obj, A)) #运行结果
#True
2. issubclass(sub, super),检查sub类是否是super类的派生类(子类)
class A:
pass class B(A):
pass print(issubclass(B, A)) #运行结果
#True
二、反射
1. 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2. python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)。
class People:
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age def info(self):
print('%s is %d years old' % (self.name, self.age)) p = People('jack', 18) #hasattr(obj, name),检查属性
print(hasattr(p, 'info')) #检查对象p是否有‘info’属性,结果True
print(hasattr(p, 'name')) #检查对象p是否有‘name’属性,结果True #getattr(obj, name)获取属性
print(getattr(p, 'name')) #获得对象p的‘name’属性,结果:返回p.name的值,即jack
print(getattr(p, 'info')) #获得对象p的‘info’属性,结果:返回p.info的值,
# 即绑定方法info的内存地址:<bound method People.info of <__main__.People object at 0x000001B64317ABE0>>
getattr(p, 'info')() #由于getattr返回的是对象方法属性的内存地址,加()就可以调用,结果:jack is 18 years old #setattr(x, y, v)设置属性
setattr(p, 'age', 21) #修改对象p的‘age’属性,结果:p.age的值变为21
setattr(p, 'sex', 'male') #新增对象p的‘sex’属性,结果:p.sex的值为male
print(p.__dict__) #查看对象p的数据属性,结果:{'name': 'jack', 'age': 21, 'sex': 'male'} #delattr(x, y)删除属性
delattr(p, 'sex') #删除对象p的‘sex’属性
print(p.__dict__) #结果:{'name': 'jack', 'age': 21}
四个可以实现自省的函数:hasattr(obj, name);getattr(obj, name);setattr(x, y, v);delattr(x, y)
#类也是对象
class Foo(object):
staticField = "old boy" def __init__(self):
self.name = 'wupeiqi' def func(self):
return 'func' @staticmethod
def bar():
return 'bar' print(getattr(Foo, 'staticField')) #获取类的'staticField'属性,结果:old boy print(getattr(Foo, 'func')) #获取类的'func'属性,结果:<function Foo.func at 0x0000018156FBB950>
print(getattr(Foo, 'func')('self')) #加()调用方法,结果:func print(getattr(Foo, 'bar')) #获取类的'bar'属性,结果:<function Foo.bar at 0x00000192D2AFB9D8>
print(getattr(Foo, 'bar')()) #加()调用方法,结果:bar
类也是对象,能够应用反射
#反射当前模块成员
#!/usr/bin/env python
# -*- coding:utf-8 -*- import sys def s1():
print('s1') def s2():
print('s2') this_module = sys.modules[__name__] print(this_module) #结果:<module '__main__' from '......'>
print(hasattr(this_module, 's1')) #结果:True
print(getattr(this_module, 's2')) #结果:<function s2 at 0x0000020590EAB8C8>
getattr(this_module, 's2')() #结果:s2
模块也是对象,能够应用反射
3. 反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
class FtpClient:
'ftp客户端,但是还么有实现具体的功能'
def __init__(self,addr):
print('正在连接服务器[%s]' %addr)
self.addr=addr ##############################
#不影响lili的代码编写 from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
func_get=getattr(f1,'get')
func_get()
else:
print('---->不存在此方法')
print('处理其他的逻辑')
好处二:动态导入模块(基于反射当前模块成员)
#两种导入用户输入模块得方法,官方推荐方法2
#方法1
m = input('input your module:') #用户输入要导入的模块名,以time模块为例
m1 = __import__(m)
print(m1) #结果:<module 'time' (built-in)>
print(m1.time()) #结果:1493023753.0157707,当前时间 #方法2
import importlib #先导入importlib模块
t = importlib.import_module(m)
print(t) #结果:<module 'time' (built-in)>
print(t.time()) #结果:1493023753.0238242,当前时间
三、内置attr
class Foo:
x = 1
def __init__(self, y):
self.y = y def __getattr__(self, item):
print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value #这就无限递归了
self.__dict__[key] = value #应该使用它 def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item) #应该使用它 #__setattr__添加/修改属性会触发它的执行
f1 = Foo(10) #因为重写了__setattr__,凡是赋值操作都会触发它的运行
print(f1.__dict__) #结果:----> from setattr {'y': 10}
f1.z = 3 #添加属性
print(f1.__dict__) #结果:----> from setattr {'y': 10, 'z': 3} #__delattr__删除属性的时候会触发
f1.__dict__['a'] = 3 #我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a #触发__delattr__
print(f1.__dict__) #结果:----> from delattr {'y': 10, 'z': 3} #__getattr__只有在使用对象调用属性且属性不存在的时候才会触发
print(f1.y) #属性存在,结果:10
f1.a #属性a不存在,触发__getattr__,结果:----> from getattr:你找的属性不存在
四、二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
#二次加工标准类型(基于继承实现)
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
def append(self, p_object):
' 派生自己的append:加上类型检查'
if not isinstance(p_object, int):
raise TypeError('must be int')
super().append(p_object) @property
def mid(self):
'新增自己的属性'
index = len(self)//2
return self[index] l = List([1, 2, 3, 4])
print(l)
l.append(5)
print(l) #结果:[1, 2, 3, 4, 5]
# l.append('1111111') #报错,必须为int类型 print(l.mid) #结果:3 #其余的方法都继承list的
l.insert(0, -123) #插入元素
print(l) #结果:[-123, 1, 2, 3, 4, 5]
l.clear() #清空列表
print(l) #结果:[]
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
# 授权示范
import time class FileHandle:
def __init__(self, filename, mode='r', encoding='utf-8'):
self.file = open(filename, mode, encoding=encoding) #获得文件句柄 def write(self, line): #重新定义write方法,新增添加时间的功能
t = time.strftime('%Y-%m-%d %T')
self.file.write('%s %s' % (t, line)) def __getattr__(self, item): #文件操作的其它属性在FileHandle类中找不到时,触发__getattr__
return getattr(self.file, item) f1 = FileHandle('b.txt', 'w+') #新建文件b.txt,获得文件句柄,赋给对象f1
f1.write('你好啊') #调用类中的定制方法write
f1.seek(0) #重置文件位置于文首,触发__getattr__,正常调用
print(f1.read()) #打印文件内容,触发__getattr__,正常调用,结果:2017-04-24 17:30:37 你好啊
f1.close() #关闭文件,触发__getattr__,正常调用
五、__getattribute__
__getattribute__是访问属性的方法,我们可以通过方法重写来扩展方法的功能。
当获取属性时,直接return object.__getattribute__(self, *args, **kwargs)
如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可。如果不加的话,返回的是函数引用地址。
class Foo:
def __init__(self, x):
self.x = x
self.y = 100 def __getattr__(self, item): #属性不存在时触发执行
print('getattr')
if item == 'y':
return 'y = 100'
else:
return "No %s attribute" %item def __getattribute__(self, item): #属性存不存在都会触发执行,而且当与__getattr__同时存在时,仅执行自己,除非抛出错误后,会执行__getattr__
print('__getattribute__ is called')
if item == 'x':
return 'x = %s' %(object.__getattribute__(self, item)) #返回属性
else:
raise AttributeError("No 'x' attribute") #当抛出错误时,会去执行__getattr__ f = Foo(10)
print(f.x) #object存在‘x’属性,运行结果:__getattribute__ is called x = 10
print(f.y) #object存在‘y’属性,触发__getattribute__,不符合if条件,抛出错误,会去执行__getattr__
#运行结果:__getattribute__ is called,getattr,y = 100
print(f.z) #object不存在‘z’属性,但还会触发_getattribute__,抛出错误,触发__getattr__,返回return的值
#运行结果:__getattribute__ is called,getattr,No z attribute
六、__setitem__,__getitem,__delitem__
触发机制与attr一致,只是将对象操作属性模拟为字典的格式:
# 把对象操作属性模拟成字典的格式
class Foo:
def __init__(self, name):
self.name = name def __getitem__(self, item):
return self.__dict__[item] def __setitem__(self, key, value):
self.__dict__[key] = value def __delitem__(self, key):
self.__dict__.pop(key) f = Foo('egon') #实例化 print(f.name) #.的方式调用,运行结果:egon
print(f['name']) #字典方式调用,运行结果:egon f.age1 = 18
f['age'] = 21
print(f.__dict__) #运行结果:{'name': 'egon', 'age1': 18, 'age': 21} del f['age1']
del f.age
print(f.__dict__) #运行结果:{'name': 'egon'}
把对象操作属性模拟成字典的格式
七、__str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__;自定制格式化字符串__format__
# _*_coding:utf-8_*_
__author__ = 'Linhaifeng'
format_dict = {
'nat': '{obj.name}-{obj.addr}-{obj.type}', # 学校名-学校地址-学校类型
'tna': '{obj.type}:{obj.name}:{obj.addr}', # 学校类型:学校名:学校地址
'tan': '{obj.type}/{obj.addr}/{obj.name}', # 学校类型/学校地址/学校名
} class School:
def __init__(self, name, addr, type):
self.name = name
self.addr = addr
self.type = type def __repr__(self):
return 'School(%s,%s)' % (self.name, self.addr) def __str__(self):
return '(%s,%s)' % (self.name, self.addr) def __format__(self, format_spec):
if not format_spec or format_spec not in format_dict:
format_spec = 'nat'
fmt = format_dict[format_spec]
return fmt.format(obj=self) s1 = School('oldboy1', '北京', '私立')
print('from repr: ', repr(s1)) #运行结果:from repr: School(oldboy1,北京)
print('from str: ', str(s1)) #运行结果:from str: (oldboy1,北京)
print(s1) #默认以‘str’定义的方式输出;运行结果:(oldboy1,北京) '''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1, 'nat')) #指定格式‘nat’输出;运行结果:oldboy1-北京-私立
print(format(s1, 'tna')) #指定格式‘tna’输出;运行结果:私立:oldboy1:北京
print(format(s1, 'tan')) #指定格式‘tan’输出;运行结果:私立/北京/oldboy1
print(format(s1, 'asfdasdffd')) #其它情况默认以‘nat’输出;运行结果:oldboy1-北京-私立
自定义格式化输出实例
八、__slots__
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name
和age
属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。 ''' class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 s = Student() # 创建新的实例
s.name = 'jack' # 绑定属性'name'
s.age = 21 # 绑定属性'age'
#s.score = 99 # 绑定属性'score';报错:'Student' object has no attribute 'score' #使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
class G(Student):
pass g = G() # 创建新的实例
g.name = 'jack' # 绑定属性'name'
g.age = 21 # 绑定属性'age'
g.score = 99 # 绑定属性'score'
限制实例的绑定属性,当前类有效
九、__next__和__iter__实现迭代器协议
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己 def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值 for i in Fib():
print(i)
斐波那契数列
十、__doc__
返回对象的描述信息
class Foo:
"""描述信息"""
pass print(Foo.__doc__) #运行结果:描述信息 #__doc__不能被继承
class Bar(Foo):
pass print(Bar.__doc__) #运行结果:None
十一、__module__和__class__
__module__ 表示当前操作的对象在哪个模块;__class__ 表示当前操作的对象的类是什么
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#将该文件存为 a.py class C: def __init__(self):
self.name = 'a.py'
#与a.py同一目录下创建新文件 from a import C #从a模块导入类C obj = C()
print(obj.__module__) # 输出 a,即:输出模块
print(obj.__class__) # 输出 <class 'a.C'>,即:输出类
十二、__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self):
print('执行我啦') f1=Foo()
del f1 #触发__del__
print('------->') #输出结果
执行我啦
-------> ######################### class Foo: def __del__(self):
print('执行我啦') f1=Foo()
# del f1
print('------->') #输出结果
------->
执行我啦 #对于当前程序,由于print('------->')运行完后程序就结束了,在结束前,会自动触发__del__
十三、__enter__和__exit__
我们知道在操作文件对象的时候可以这么写
with open('a.txt') as f:
'代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:
def __init__(self, name):
self.name = name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕后执行__exit__') with Open('a.txt') as f:
print('=====>执行代码块') #运行结果:
"""
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕后执行__exit__
"""
上下文管理协议
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open:
def __init__(self, name):
self.name = name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕后执行__exit__')
print(exc_type)
print(exc_val)
print(exc_tb)
#return True with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('手动抛错') print('其它内容') #不会执行 #运行结果:
"""
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
raise AttributeError('手动抛错')
=====>执行代码块
with中代码块执行完毕后执行__exit__
<class 'AttributeError'>
手动抛错
AttributeError: 手动抛错
<traceback object at 0x0000027B163221C8>
"""
with语句抛错,后面语句不执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open:
def __init__(self, name):
self.name = name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕后执行__exit__')
print(exc_type)
print(exc_val)
print(exc_tb)
return True with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('手动抛错') print('其它内容')
#运行结果:
"""
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕后执行__exit__
<class 'AttributeError'>
手动抛错
<traceback object at 0x000001CB500921C8>
其它内容
"""
__exit__返回True,with后语句正常执行
用途或者说好处:
- 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
- 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关心这个问题,这将大有用处
十四、__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass def __call__(self, *args, **kwargs):
print('__call__') obj = Foo() # 执行 __init__
obj() # 执行 __call__
Foo()() # 执行 __call__
obj()触发__call__
十五、metaclass
1. 引子
class Foo:
pass f1=Foo() #f1是通过Foo类实例化的对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?
#type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print(type(Foo)) # 输出:<class 'type'>
2. 什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3. 创建类的两种方式
方式一:
class Foo:
def func(self):
print('from func')
方式二:
def func(self):
print('from func') x=1
Foo=type('Foo',(object,),{'func':func,'x':1}) # type(object_or_name, bases, dict)
4. 自定义元类
一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
当创建一个元类的对象(也是类)时的流程:
class Mymeta(type):
def __init__(self, name, bases, dic): #5 执行__init__函数,初始化对象
print('===>Mymeta.__init__') #6 打印'===>Mymeta.__init__' def __new__(cls, *args, **kwargs): #2 执行__new__函数,创建对象
print('===>Mymeta.__new__') #3 打印'===>Mymeta.__new__'
return type.__new__(cls, *args, **kwargs) #4 返回类Mymeta创建的对象Foo class Foo(object,metaclass=Mymeta): #1 类Foo是元类Mymeta的对象,在创建对象时,会先后调用类的__new__和__init__方法
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
return object.__new__(cls) #运行结果:
# ===>Mymeta.__new__
# ===>Mymeta.__init__
创建元类的对象的流程,只调用元类的new和init
重点:当通过一个类创建一个对象的时候,会先后调用类的__new__和__init__方法。元类是用来创建类的,也就是说类是元类创建的对象,所以元类的__new__和__init__方法会被调用。
当创建一个元类的子类的对象(实例)时的流程:
#元类
class MyType(type): def __init__(self, class_name, bases=None, dict=None): #5 执行__init__函数,初始化对象
print('MyType init --->') #6 打印:'MyType init --->'
print(classmethod, type(class_name)) #7 打印:<class 'classmethod'> <class 'str'>
print(bases) #8 打印(Foo的bases是object):(<class 'object'>,);
print(dict) #9 打印:对象Foo的命名空间 def __new__(cls, *args, **kwargs): #2 执行__new__(MyType, *args, **kwargs)函数,创建对象;
print('===>Mymeta new') #3 打印:===>Mymeta new
return type.__new__(cls, *args, **kwargs) #4 返回类Mymeta创建的对象Foo def __call__(self, *args, **kwargs): #11 执行__call__(Foo, *args, **kwargs)
print('MyType call --->', self, args, kwargs) #12 打印:MyType call ---> <class '__main__.Foo'> ('name',) {}
return type.__call__(self, *args, **kwargs) #13 返回由元类MyType创建好的对象Foo,此时,就要调用Foo的__new__和__init__ class Foo(object, metaclass=MyType): #1 创建元类的对象Foo,触发元类MyType的__new__和__init__
x = 111
def __init__(self, name): #17 执行:__init__(Foo.obj, 'jack')
print('Foo init') #18 打印:Foo init
self.name = name #19 赋值:self.name = 'jack' def __new__(cls, *args, **kwargs): #14 执行:__new__(cls, *args, **kwargs)
print('Foo new') #15 打印:Foo new
return object.__new__(cls) #16 返回由类Foo创建好的对象 f = Foo('jack') #10 到这一步,Foo对象已经创建好了,执行Foo('name'),相当于调用父类即MyType里的__call__方法,创建了Foo的对象,然后赋值给f
print(f.name) #20 打印:jack #运行结果:
# ===>Mymeta new
# MyType init --->
# <class 'classmethod'> <class 'str'>
# (<class 'object'>,)
# {'__module__': '__main__', '__qualname__': 'Foo', 'x': 111, '__init__': <function Foo.__init__ at 0x000001F945B748C8>, '__new__': <function Foo.__new__ at 0x000001F945B74950>}
# MyType call ---> <class '__main__.Foo'> ('jack',) {}
# Foo new
# Foo init
# jack
创建元类的对象的对象,对象名()相当于调用父类的call方法
补充:
对于元类的查找,Python有一套规则:
- Python解释器会在当前类中查找"__metaclass__"属性对于的代码,然后创建一个类对象
- 如果没有找到"__metaclass__"属性,会继续在父类中寻找"__metaclass__属性",并尝试前面同样的操作
- 如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建这个类对象
参考资料:
1. http://www.cnblogs.com/linhaifeng/articles/6204014.html
2. http://bbs.csdn.net/topics/360135494
3. http://www.cnblogs.com/wilber2013/p/4695836.html
【转】Python-面向对象进阶的更多相关文章
- Python面向对象进阶(二)
Python面向对象进阶2.html :first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba(0,0,0,.1 ...
- Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)
Python开发[第七篇]:面向对象 详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇) 上一篇<Python 面向对象(初级篇)> ...
- Python面向对象进阶和socket网络编程-day08
写在前面 上课第八天,打卡: 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __i ...
- Python面向对象进阶和socket网络编程
写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __init__(self ...
- python面向对象进阶(八)
上一篇<Python 面向对象初级(七)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...
- python面向对象进阶
前言 上节大话python面向对象对面向对象有了一些了解,这次就不用大话风格了 (ps:真心不好扯啊) isinstance与issubclass isinstance(obj,cls)检查是否obj ...
- python 面向对象进阶之内置方法
一 isinstance(obj,cls)和issubclass(sub,super) 1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(obj ...
- Python 面向对象 (进阶篇)
<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可 ...
- Python之路-python(面向对象进阶)
一.面向对象高级语法部分 1.静态方法.类方法.属性方法 2.类的特殊方法 3.反射 二.异常处理 三.Socket开发基础 一.面向对象高级语法部分 静态方法(@staticmethod) 定义:只 ...
- Python学习笔记【第十篇】:Python面向对象进阶
保护对象的属性 如果有一个对象,当需要对其进行修改属性时,有2种方法 对象名.属性名 = 数据 ---->直接修改 对象名.方法名() ---->间接修改 为了更好的保存属性安全,即不能随 ...
随机推荐
- python自动化开发-[第二十天]-form表单,CBV和FBV,序列化
1.CBV和FBV的用法 2.序列化用法 3.form表单 一.CBV和FBV 1.cbv是 class based view(基于类),fbv是function based view(基于函数) 2 ...
- Linux学习杂谈
Linux学习相关的... --------- 1.Linux是免费的2.Linux是安全稳定的3.linux是开源的,却世界的工程师都在维护系统--------------------熟悉脚本开发语 ...
- C#中 Reference Equals, == , Equals的区别
原文地址:http://blog.csdn.net/wuchen_net/archive/2010/03/23/5409327.aspx ReferenceEquals, == , Equals Eq ...
- Jenkins权限分配
做完Jenkins安装.项目自动化打包部署后,当然需要对小组成员进行项目权限的分配 1.安装插件:Role-based Authorization Strategy(安装过程就展示了): 2. 插件装 ...
- 2016vijos 1-2 股神小L(堆)
维护前i天的最优解,那么在后面可能会对前面几天的买卖情况进行调整 如果前面买入,买入的这个在后面一定不会卖出 如果前面卖出,卖出的这个可能会在后面变成买入,因为买这个,卖后面的会获得更多的收益 用一个 ...
- Docker 从入门到放弃(三)镜像使用
当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载. 下面我们来学习: 1.管理和使用本地 Docker ...
- 自学python 2.
1.T or F 1>1 or 3<4 or 4>5 and 2>1 and 9>8 or 7<6 t not 2>1 and 3<4 or 4> ...
- vue-router拦截
说明:以下均在main.js中添加. 主要思路 1.在路由分发时,检查本地缓存是否有账号信息,如果没有,跳转登陆页面,传入当前路由 2.在发送请求时,添加账号token 3.在接收请求时,检查响应的数 ...
- css的几个小技巧
本文收录css设置样式的一些小技巧 1. 设置文字在块级标签居中(包括水平居中和垂直居中) 水平居中 方法一:使用text-align text-align:center 方法二:目标标签的父级标签设 ...
- SpringBoot系列: 使用MyBatis maven插件自动生成java代码
====================================pom.xml 文件====================================需要在 pom.xml 文件增加 m ...