面向对象之元类,单例
一.eval与exect内置函数
eval内置函数的使用场景:
1.执行字符串会得到相应的执行结果 2.一般用于类型转化,得到dict、list、tuple等 # "{'a': 1, 'b': 2, 'c': 3}" => {'a': 1, 'b': 2, 'c': 3} data_str="{'a': 1, 'b': 2, 'c': 3}" res = eval(data_str) print(res, type(res)) #{'a': 1, 'b': 2, 'c': 3} <class 'dict'>
exec应用场景:
# 1.执行字符串没有执行结果(没有返回值) # 2.将执行的字符串中产生的名字形成对应的局部名称空间 s = ''' my_a = 10 my_b = 20 def __init__(self): pass @classmethod def print_msg(msg): print(msg) ''' l_dic = {} exec(s, {}, l_dic) #s是自定义字符串,包含属性和方法,{}是全局名称空间,设为空字典就行, #执行exec后s中的内容会给到局部名称空间l_dic字典中 print(l_dic) #{'my_a': 10, 'my_b': 20, '__init__': <function __init__ at 0x01EDEA50>...}
那么可以用exec创建一个对象的名称空间:
source = ''' name = 'Bob' age = 20 ''' #source是自定义字符串
class A: pass a = A() #这个类中没有init函数,实例化的对象没有创建名称空间
dic = {} exec(source, {}, dic) #执行exec后source中的内容会给到局部名称空间dic字典中 a.__dict__ = dic # 对象a的名称空间本来为空,现在将dic替换掉a的名称空间 print(a.__dict__) # {'name': 'Bob', 'age': 20} print(a.name) # Bob print(a.age) # 20
二.元类的基本定义:
Python中万物皆对象,所有用来创建对象的类,本身也是对象,类是type类的对象
type类叫做元类,是所有元类(包括自定义元类)的基类
元类:造类的类 - 也就是类的类
-- 控制类的产生
-- 控制类的对象的产生
三.自定义元类:
那么我们先来看一下type中有哪几个参数:
class MyMeClass(type):
#这是一个自定义元类,控制User类的产生,这里主要看参数,下面会详细介绍 def __new__(cls, *args,**kwargs): print(args) # ('User', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'User', '__init__': <function User.__init__ at 0x07F8D930>}) #第一个是类名,第二是基类,第三个是名称空间
print(kwargs) # {} return type.__new__(cls,*args,**kwargs) class User(object,metaclass=MyMeClass): def __init__(self,name ): self.name = name
用type造个类看看,利用 type(classname, basename, namespace)直接造类:
s = ''' wudaorong="小可爱" ''' namespace ={} exec(s,{},namespace) Student = type('Student',(object,),namespace) #基类可能会继承多个,所以用元组括起来,可以什么都不写,则默认是object print(Student.__dict__) stu=Student() print(stu.wudaorong)
自定义元类都是继承type的,先来看它的__init__方法:
class MyMeta(type): # 自定义元类,重写init方法的目的: # 1.该方法是从type中继承来的,所以参数同type的init # 2.最终的工作,如果开辟空间,还是要借助type # 3.在交给type最终完成工作之前,可以对类的创建加以限制 def __init__(cls, class_name, bases, namespace): # 继承type的init方法 super().__init__(class_name, bases, namespace) #可以查看每个控制的类的各参数 print(cls) print(class_name) print(bases) print(namespace) #自定义除type外新加要求: # 需求1:由给元类控制的类的类名必须首字母大写 if not class_name.istitle(): raise NameError('名字首字母必须大写') # 需求2:定义类是必须明确父类 if len(bases) == 0: raise Exception('父类必须明确') #创建类方式一: s=''' def __init__(self, name, age): self.name = name self.age = age ''' namespace={} exec(s,{},namespace) Student = MyMeta('Student', (object,), namespace ) #创建类方式二: class Student(object, metaclass=MyMeta): def __init__(self, name, age): self.name = name self.age = age #继承仍生效,同时让它也受MyMeta控制 class Xuesheng(Student, metaclass=MyMeta): pass
除了控制类,我们还可以对它实例化出来的对象进行控制(__call__方法):
class MyMeta(type): # 自定义元类,重写call方法的目的: # 1.被该元类控制的类生成对象,会调用元类的call方法 # 2.在call中的返回值就是创建的对象 # 3.在call中 # -- 通过object开辟空间产生对象 # -- 用被控制的类回调到自己的init方法完成名称空间的赋值 # -- 将修饰好的对象反馈给外界 def __call__(cls, *args, **kwargs): print('我是小胖啊') obj = object.__new__(cls) # 需求:所有通过该元类控制的类产生的对象都有meta_name该属性 obj.meta_name = cls.__name__ # obj.name = args[0] # 调回当前被控制的类自身的init方法,完成名称空间的赋值 cls.__init__(obj, *args, **kwargs) return obj class Student(object, metaclass=MyMeta): def __init__(self, name, age): self.name = name self.age = age #继承仍生效,同时让它也受MyMeta控制 class Xuesheng(Student, metaclass=MyMeta): hobby='打游戏' xs=Xuesheng('wdr',18) cls_name=xs.meta_name name=xs.name hobby=xs.hobby print(cls_name,name,hobby) print(xs.__dict__,Xuesheng.__dict__)
注: 实例化在走init方法先会被自定义元类的__call__方法修饰后才完成:
class Foo: def __init__(self): print('__init__') def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
可能对__new__方法有疑惑,再来看看__new__:
1. 类一旦重写__new__方法,该类的实例化由__new__来控制,__init__会失效 class A: def __init__(self,name): self.name=name def __new__(cls, name): cls.name = name #下面的的a其实是A元类实例化出来的,即a就是A这个类本身 #把__new__写在某个类里面就是要自定义这个类 a= A('小胖') print(A.__dict__)
打印a.__dict__会报错,因为没有走__init__
2.如果还想控制这个类的对象,要借助object.__new__:
class A:
def __init__(self,name):
self.name=name
def __new__(cls, name,*args, **kwargs):
cls.name = name
obj = object.__new__(cls)
# 先利用object.__new__开辟了一个对象内存空间,然后赋予属性实例化
#这里可以可以直接给属性赋值,没有则走__init__,必须把这个对象返回
return obj
a= A('小胖')
print(A.__dict__,a.__dict__)
四.单例
单例:一个类只能产生一个实例 为什么要有单例: 1.该类需要对象的产生 2.对象一旦产生,在任何位置再实例化对象,只能得到第一次实例化出来的对象
3.在对象唯一创建后,可以通过属性修改或利用方法间接修改属性,来完成数据的更新,但不能通过实例化方式更新数据
方式1 :任何文件在任何地方通过导入拿到的是唯一对象
class Songs(): pass # s1 = Songs() # s2 = Songs() # print(s1, s2), 产生不同对象 # 对外提供的对象 song = Songs() #外界通过导包拿到这个对象都是一样的 from part1.SingleModule import song print(song) from part1.SingleModule import song print(song) from part1.SingleModule import song print(song)
方式2:用类方法来获取唯一对象
class Songs(): __instance = None @classmethod def getInstance(cls): # 对象没有创建返回,有直接返回 if cls.__instance == None: cls.__instance = cls() #把第一次实例化对象的内存地址传给类__instance属性 return cls.__instance s1 = Songs.getInstance() s2 = Songs.getInstance() print(s1, s2)
方式3:利用obj.__new__方法:
class Songs:
__instance = None
def __new__(cls, song_name, *args, **kwargs):
if cls.__instance == None:
cls.__instance = object.__new__(cls)
cls.__instance.song_name = song_name
#先利用object.__new__开辟了一个对象内存空间,然后赋予属性实例化
return cls.__instance
#通过类方法可以修改属性值
def change_song(self, song_name):
self.song_name = song_name
s1 = Songs('菊花台')
s2 = Songs('东风破')
print(s1.song_name, s2.song_name) # 菊花台 菊花台
s2.change_song('青花瓷')
print(s1.song_name, s2.song_name) # 青花瓷 青花瓷
方式4.装饰器完成单例:
def outer(cls): _instance = None def inner(*args, **kwargs): nonlocal _instance if _instance == None: _instance = cls(*args, **kwargs) return _instance return inner @outer # Songs = outer(Songs) class Songs: pass s1 = Songs() s2 = Songs() print(s1, s2)
方式5.通过元类完成单例:
class SingleMeta(type): __instance = None def __call__(cls, *args, **kwargs): if SingleMeta.__instance == None: SingleMeta.__instance = object.__new__(cls) cls.__init__(SingleMeta.__instance, *args, **kwargs) return SingleMeta.__instance class Songs(metaclass=SingleMeta): def __init__(self): pass pass s1 = Songs() s2 = Songs() print(s1, s2)