内置函数(isinstance,issubclass)反射 __str__ __del__ __call__ 元类 单例
17 Apr 18
一、内置函数补充 (isinstance 和 issubclass)
class Foo:
pass
obj=Foo()
print(type(obj) is Foo) #用type可以完成但这不是type的主要用途,顾不推荐
print(isinstance(obj,Foo)) # 推荐使用该函数来判断一个函数的类型
print(isinstance('abc',str)) # ‘abc’是否是类str的一个对象/函数
print(isinstance(123,int))
print(issubclass(Foo,object)) #Foo是否是object的子类
一、反射
通过字符串来操作类与对象的属性,这种操作称为反射;反射也是内置函数
下述四个函数是专门用来操作类与对象属性的
class People:
country="China"
def __init__(self,name):
self.name=name
def tell(self):
print('%s is aaa' %self.name)
obj=People('egon')
1、hasattr
print(hasattr(People,'country')) #可用来操纵类 # print('country' in People.__dict__)
print(hasattr(obj,'name')) #可用来操纵对象
print(hasattr(obj,'country'))
print(hasattr(obj,'tell'))
2、getattr
print(getattr(People,'country1',None)) #如果没有返回None;若不写,没有报错
f=getattr(obj,'tell',None) #obj.tell
f() #obj.tell()
3、setattr
setattr(People,'x',111) #People.x=111
print(People.x)
setattr(obj,"age",18) #obj.age=18
print(obj.__dict__)
4、delattr
delattr(People,"country") #del People.country
print(People.__dict__)
delattr(obj,"name") #del obj.name
print(obj.__dict__)
#用户用input输入了字符串形式的指令,可以被反射以操纵类和对象的属性
class Foo:
def run(self):
while True:
cmd=input('cmd>>: ').strip()
if hasattr(self,cmd):
func=getattr(self,cmd)
func()
def download(self):
print('download....')
def upload(self):
print('upload...')
obj=Foo()
obj.run()
三、__str__方法
__xxx__是满足一定条件时自动触发
__str__ 当打印对象时自动触发,可用来定制打印格式
class People:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def __str__(self):
return '<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex)
obj=People('egon',18,'male')
print(obj) #print(obj.__str__()),若没有__str__,返回一个内存地址(原生状态)
l=list([1,2,3])
print(l) #[1, 2, 3], python帮忙优化的,否则返回一个内存地址
四、__del__方法
__del__在删除对象时自动触发
import time
class People:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def __del__(self): #在对象被删除的条件下,自动执行
print('__del__')
obj=People('egon',18,'male')
del obj #obj.__del__() #若没有这一行,睡2s后会自动触发__del__,因为程序结束前会删除对象(清除资源)
time.sleep(2)
__del__的主要用途是回收系统资源
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
self.conn=connect(ip,port) #申请系统资源, 此行为伪代码
def __del__(self):
self.conn.close() #回收系统资源
obj=Mysql('1.1.1.1',3306)
class MyOpen:
def __init__(self,filepath,mode="r",encoding="utf-8"):
self.filepath=filepath
self.mode=mode
self.encoding=encoding
self.fobj=open(filepath,mode=mode,encoding=encoding)
def __str__(self):
msg="""
filepath:%s
mode:%s
encoding:%s
""" %(self.filepath,self.mode,self.encoding)
return msg
def __del__(self):
self.fobj.close() #回收系统资源
f=MyOpen('aaa.py',mode='r',encoding='utf-8')
print(f.filepath,f.mode,f.encoding)
print(f)
print(f.fobj)
res=f.fobj.read()
print(res)
以下四个皆为绑定对象的方法,在满足一定条件时,自动触发
__init__
__str__
__del__
__call__
五、元类
exec执行字符串中的代码,将产生的名字存入global_dic(全局名称空间)和local_dic(局部名称空间)
code="""
global x
x=0
y=2
"""
global_dic={'x':100000}
local_dic={}
exec(code,global_dic,local_dic)
print(global_dic)
print(local_dic) #没有声明都是局部的
code="""
x=1
y=2
def f1(self,a,b):
pass
"""
local_dic={}
exec(code,{},local_dic)
print(local_dic) #{'x': 1, 'y': 2, 'f1': <function f1 at 0x101c60e18>}
a、 一切皆对象
元类:类的类就是元类,内置元类type是用来专门产生class定义的类的
我们用class定义的类来产生我们自己的对象的
class Chinese: #Chinese=type(...)
country="China"
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def speak(self):
print('%s speak Chinese' %self.name)
print(Chinese) #<class '__main__.Chinese'>
p=Chinese('egon',18,'male')
print(type(p)) #对象p的类是类chinese <class '__main__.Chinese'>
print(type(Chinese)) #对象(类chinese)的类是类type <class 'type'>
class Foo: #Foo=type(...)
pass
print(type(Foo)) #<class 'type'>
f=Foo
l=[Foo,]
print(l) #[<class '__main__.Foo'>]
b、 用内置的元类type,来实例化得到我们的类
class_name='Chinese'
class_bases=(object,)
class_body="""
country="China"
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def speak(self):
print('%s speak Chinese' %self.name)
"""
class_dic={}
exec(class_body,{},class_dic)
print(class_name,class_bases,class_dic) # 类的三大要素
Chinese=type(class_name,class_bases,class_dic)
print(Chinese)
p=Chinese('egon',18,'male')
print(p.name,p.age,p.sex)
c、 储备知识 __call__
调用对象,则会自动触发对象下的绑定方法__call__的执行,然后将对象本身当作第一个参数传给self,将调用对象时括号内的值传给*args与**kwargs
class Foo:
def __init__(self):
pass
def __str__(self):
return '123123'
def __del__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__',args,kwargs)
obj=Foo()
print(obj) #123123
obj(1,2,3,a=1,b=2,c=3) #__call__ (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}
d、 自定义元类
class Mymeta(type):
# 来控制类Foo的创建
def __init__(self,class_name,class_bases,class_dic): #self=Foo
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')
if not class_dic.get('__doc__'): #对象.__doc__看类注释,无注释返回None
raise TypeError('类中必须写好文档注释')
super(Mymeta,self).__init__(class_name,class_bases,class_dic) #其他的遗传object
# 控制类Foo的调用过程,即控制实例化Foo的过程
def __call__(self, *args, **kwargs): #self=Foo,args=(1111,) kwargs={}
obj=object.__new__(self) #1 造一个空对象obj
self.__init__(obj,*args,**kwargs) #2、调用Foo.__init__,将obj连同调用Foo括号内的参数一同传给__init__
return obj
class Foo(object,metaclass=Mymeta): # Foo=Mymeta('Foo',(object,),class_dic)
"""
文档注释
"""
x=1
def __init__(self,y):
self.y=y
def f1(self):
print('from f1')
obj=Foo(1111) #Foo.__call__()
print(obj) #<__main__.Foo object at 0x104501cf8>
print(obj.y) #1111
print(obj.f1) #<bound method Foo.f1 of <__main__.Foo object at 0x104501cf8>>
print(obj.x) #1
e、 单例模式
import settings
class MySQL:
__instance=None #将instance隐藏使外部不能直接调用
def __init__(self,ip,port):
self.ip=ip
self.port=port
@classmethod
def singleton(cls):
if not cls.__instance:
obj=cls(settings.IP, settings.PORT)
cls.__instance=obj
return cls.__instance
obj1=MySQL('1.1.1.2',3306)
obj2=MySQL('1.1.1.3',3307)
obj3=MySQL('1.1.1.4',3308)
#每次取用麻烦,且即使是相同内容,每次亦产生一个新的内存空间
obj4=MySQL(settings.IP,settings.PORT)
print(obj4.ip,obj4.port)
#单例模式
obj4=MySQL.singleton()
obj5=MySQL.singleton()
obj6=MySQL.singleton()
print(obj4 is obj5 is obj6) #True
f. 基于元类实现单例模式的三种方式
单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间;如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
#settings.py文件内容如下
HOST='1.1.1.1'
PORT=3306
#方式一:定义一个类方法实现单例模式
import settings
class Mysql:
__instance=None
def __init__(self,host,port):
self.host=host
self.port=port
@classmethod
def singleton(cls):
if not cls.__instance:
cls.__instance=cls(settings.HOST,settings.PORT)
return cls.__instance
obj1=Mysql('1.1.1.2',3306)
obj2=Mysql('1.1.1.3',3307)
print(obj1 is obj2) #False
obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True
#方式二:定制元类实现单例模式
import settings
class Mymeta(type):
def __init__(self,name,bases,dic): #定义类Mysql时就触发
# 事先先从配置文件中取配置来造一个Mysql的实例出来
self.__instance = object.__new__(self) # 产生对象
self.__init__(self.__instance, settings.HOST, settings.PORT) # 初始化对象
# 上述两步可以合成下面一步
# self.__instance=super().__call__(*args,**kwargs)
super().__init__(name,bases,dic)
def __call__(self, *args, **kwargs): #Mysql(...)时触发
if args or kwargs: # args或kwargs内有值
obj=object.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
return self.__instance
class Mysql(metaclass=Mymeta):
def __init__(self,host,port):
self.host=host
self.port=port
obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3)
obj4=Mysql('1.1.1.4',3307)
#方式三:定义一个装饰器实现单例模式
import settings
def singleton(cls): #cls=Mysql
_instance=cls(settings.HOST,settings.PORT)
def wrapper(*args,**kwargs):
if args or kwargs:
obj=cls(*args,**kwargs)
return obj
return _instance
return wrapper
@singleton # Mysql=singleton(Mysql)
class Mysql:
def __init__(self,host,port):
self.host=host
self.port=port
obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3) #True
obj4=Mysql('1.1.1.3',3307)
obj5=Mysql('1.1.1.4',3308)
print(obj3 is obj4) #False