Fork me on GitHub

反射,元类

 

一 isinstance与issubstance:

isinstance判断的是obj是否是cls的对象:

class Foo:
    def __init__(self,name):
        self.name=name

    def tell(self):
        print('self')
obj=Foo('egon')

print(isinstance(obj,Foo))

issubstance判断的是子类与父类的关系与否:必须是类与类之间的比较

issubstance(Foo,object)

二  反射:

什么叫反射:python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

内置方法:

下面是反射的一些操作:hasattr,getattr,setattr,delattr:

class
Foo: country='China' def __init__(self,name): self.name=name def tell(self): print('tell info') obj=Foo('egon') # print(hasattr(Foo,'country')) # print(hasattr(Foo,'tell')) # print(hasattr(obj,'country')) # print(hasattr(Foo,'tell')) # print(getattr(Foo,'country'))
print(getattr(obj,'tell',None))      #从obj中取‘tell’属性,如果没有的话会报错,所以我们为了防止报错应该在后面加上‘None’
setattr(Foo,'age',19) # 为Foo设置age=19
print(Foo.__dict__) # 类对象调用__dict__方法得到的是其下所有的属性以及对应的值。
delattr(Foo,'age') # 删除Foo中的属性age
print(Foo.__dict__)

下面我们说一种getattr与hasattr连用的方式:

class Foo:
#     def __init__(self):
#       while True:
#         cmd=input(':').strip()
#         if hasattr(self,cmd):
#             func=getattr(self,cmd)
#             func()
#     def download(self):
#         print('download...')
#     def upload(self):
#         print('upload....')
#

三  内置的方法:__str__  ,__del__  ,__call__,__getattr__是一些满足条件自动触发。其中__getattr__是在对象调一个不存在的属性的时候执行的。

__str__所表达的意思就是:

在print打印类的对象的时候会自动触发,__str__下的代码运行。

class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __str__(self):
        print('=====================================')
        return '''
        name:%s
        age:%s
        sex:%s
        '''%(self.name,self.age,self.sex)

obj=People('feng',18,'male')
print(People)# 
print(obj)#相当于打印print(obj.__dict__())

__del__:

一般用于回收系统资源,在内存中的对象被删除之前,会触发__del__先回收系统资源。

例1:
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('eogn',18,'female')
del obj
time.sleep(5)
例2:
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=open('a.txt',mode='r',encoding='utf-8')

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)
del的应用

 

__call__:调用对象就会触发__call__,也就是在对象后面加括号()。

class Foo:
    def __init__(self):
        pass
    def __str__(self):
        return '324'
    def __del__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__',*args,**kwargs)

obj=Foo()
# print(obj)
obj(1,2,3,a=2)

四  元类

元类:类的类就是元类

我们一般用class来定义一个类,从而实例化出一系列对象,

内置的元类type是用来专门产生class定义的类的。那么从这里我们可以看出类就是类型。

1  补充exec:

code='''
x=1
y=2
z=3
'''
global_dic={}
local_dic={}
字符串中的代码没有声明全局,那么就是局部global x---》x=12
exec (code,{全局名称空间},{局部名称空间})
exec(code,global_dic,local_dic)
exec会运行code的代码将产生的全局的名字放到全局名称空间
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 0x000000FEFBB12E18>}
exec是用来运行字符串中的代码,产生的名字放在名称空间中,如果声明了全局名称空间,那么就放到全局里面,
否则就默认为局部名称。
exec

创建类的两种方式:

第一种:直接用class产生

class Chinese:
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def talk(self):
        print('%s is talking' %self.name)

print(type(Chinese))#<class 'type'>我们可以看出Chinese就是类,

第二种:手动模拟class创建类的过程,通过元类type来产生

type中有三个参数:在实例化的过程type(what,bases=None,dict=None)分别代表了:类名,类的基类,类的名称空间。也就是类的三要素。

 

类的组成:类名,类的基类,类的名称空间,其实就是在定义阶段产生的名字放在类所在的名称空间。
类名
class_name='Chinese'
类的基类
class_bases=(object,)#这里必须要加一个逗号’,‘
类体
class_body="""
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def talk(self):
    print('%s is talking' %self.name)
"""
class_dic={}
exec(class_body,globals(),class_dic)步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),
我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),
生成类的局部名称空间,即填充字典

print(class_dic)输出结果为{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo,通过type得到的就是一个类,
跟我们用class来定义是一样的。
print(Foo)
print(type(Foo))
print(isinstance(Foo,type))
# 得到的结果为:
'''
<class '__main__.Chinese'>
<class 'type'>
True
'''

我们还可以自定义一个元类type:

#4 、自定义元类:
# class Mymeta(type):
#     # 来控制类Foo的创建
#     def __init__(self,class_name,class_bases,class_dic): #self=Foo
#         # print(class_name)
#         # print(class_bases)
#         # print(class_dic)
#         if not class_name.istitle():
#             raise TypeError('类名的首字母必须大写傻叉')
#
#         if not class_dic.get('__doc__'):
#             raise TypeError('类中必须写好文档注释,大傻叉')
#
#         super(Mymeta,self).__init__(class_name,class_bases,class_dic)
#
#     # 控制类Foo的调用过程,即控制实例化Foo的过程
#     def __call__(self, *args, **kwargs): #self=Foo,args=(1111,) kwargs={}
#         # print(self)
#         # print(args)
#         # print(kwargs)
#
#         #1 造一个空对象obj
#         obj=object.__new__(self)
#
#         #2、调用Foo.__init__,将obj连同调用Foo括号内的参数一同传给__init__
#         self.__init__(obj,*args,**kwargs)
#
#         return obj
#
#
#
# #Foo=Mymeta('Foo',(object,),class_dic)
# class Foo(object,metaclass=Mymeta):
#     """
#     文档注释
#     """
#     x=1
#     def __init__(self,y):
#         self.Y=y
#
#     def f1(self):
#         print('from f1')
#
#
# obj=Foo(1111) #Foo.__call__()
#
# # print(obj)
# # print(obj.y)
# # print(obj.f1)
# # print(obj.x)
自定义元类

单例模式:也就是说当很多个实例化产生对象的结果是一样的情况下,我们可以使用单例模式,通过绑定@classmethod,让相同的对象都指向同一个地址,节省内存空间。

import settings

class MySQL:
    __instance=None
    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)

 

单例模式三种方法:

import setting

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(setting.HOST,setting.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 setting

class Mymeta(type):
    def __init__(self,name,bases,dic): #定义类Mysql时就触发

        # 事先先从配置文件中取配置来造一个Mysql的实例出来
        self.__instance = object.__new__(self)  # 产生对象
        self.__init__(self.__instance, setting.HOST, setting.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 setting

def singleton(cls): #cls=Mysql
    _instance=cls(setting.HOST,setting.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) #Fals
View Code

 

posted @ 2018-04-16 20:31  道阻切长  阅读(218)  评论(3编辑  收藏  举报