面向对象之元类,单例

一.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)
 
原文地址:https://www.cnblogs.com/sima-3/p/11058418.html