[Python之路] 元类(引申 单例模式)

一、类也是对象

当我们定义一个变量或者函数的时候,我们可以在globals()的返回值字典中找到响应的映射:

def A():
    print("This is function A")


myname = "Leo"


print(globals())

我们可以得到以下结果:

{
    '__name__': '__main__',
    '__doc__': None,
    '__package__': None,
    '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021F09971CC0>,
    '__spec__': None,
    '__annotations__': {},
    '__builtins__': <module 'builtins' (built-in)>,
    '__file__': 'D:/pycharm_project/leo1201/basic_class/test.py',
    '__cached__': None,
    'A': <function A at 0x0000021F099AC1E0>,
    'myname': 'Leo'
}

我们可以发现我们定义的函数A和变量myname都在这个字典中,这个字典中记录的映射,实际上就是全局变量(也就是可以直接调用的对象)。

 

那么我们定义的类是什么呢:

class B(object):
    pass


print(globals())

得到的结果:

{
    '__name__': '__main__',
    '__doc__': None,
    '__package__': None,
    '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021F09971CC0>,
    '__spec__': None,
    '__annotations__': {},
    '__builtins__': <module 'builtins' (built-in)>,
    '__file__': 'D:/pycharm_project/leo1201/basic_class/test.py',
    '__cached__': None,
    'B': <class '__main__.B'>
}

我们可以看到,类B也在该字典中。所以,我们说类实际上也是一个全局对象,只是这个对象的功能可以生成其他的对象而已。

 

二、内建属性

在上面所看到的globals()返回的字典中,我们并未看到我们常用的print()等直接调用的函数,这是因为这些内建函数都在'__builtin__'中:

global_dict = globals()
print(global_dict['__builtins__'])  # 打印 <module 'builtins' (built-in)>

我们打印一下__builtin__字典:

global_dict = globals()
print(global_dict['__builtins__'].__dict__)

得到一个很长的字典:

{
    '__name__': 'builtins',
    '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.",
    '__package__': '',
    '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
    '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),
    '__build_class__': <built-in function __build_class__>,
    '__import__': <built-in function __import__>,
    'abs': <built-in function abs>,
    'all': <built-in function all>,
    'any': <built-in function any>,
    'ascii': <built-in function ascii>,
    'bin': <built-in function bin>,
    'breakpoint': <built-in function breakpoint>,
    'callable': <built-in function callable>,
    'chr': <built-in function chr>,
    'compile': <built-in function compile>,
    'delattr': <built-in function delattr>,
    'dir': <built-in function dir>,
    'divmod': <built-in function divmod>,
    'eval': <built-in function eval>,
    'exec': <built-in function exec>,
    'format': <built-in function format>,
    'getattr': <built-in function getattr>,
    'globals': <built-in function globals>,
    'hasattr': <built-in function hasattr>,
    'hash': <built-in function hash>,
    'hex': <built-in function hex>,
    'id': <built-in function id>,
    'input': <built-in function input>,
    'isinstance': <built-in function isinstance>,
    'issubclass': <built-in function issubclass>,
    'iter': <built-in function iter>,
    'len': <built-in function len>,
    'locals': <built-in function locals>,
    'max': <built-in function max>,
    'min': <built-in function min>,
    'next': <built-in function next>,
    'oct': <built-in function oct>,
    'ord': <built-in function ord>,
    'pow': <built-in function pow>,
    'print': <built-in function print>,
    'repr': <built-in function repr>,
    'round': <built-in function round>,
    'setattr': <built-in function setattr>,
    'sorted': <built-in function sorted>,
    'sum': <built-in function sum>,
    'vars': <built-in function vars>,
    'None': None, 'Ellipsis': Ellipsis,
    'NotImplemented': NotImplemented,
    'False': False, 'True': True,
    'bool': <class 'bool'>,
    'memoryview': <class 'memoryview'>,
    'bytearray': <class 'bytearray'>,
    'bytes': <class 'bytes'>,
    'classmethod': <class 'classmethod'>,
    'complex': <class 'complex'>,
    'dict': <class 'dict'>,
    'enumerate': <class 'enumerate'>,
    'filter': <class 'filter'>,
    'float': <class 'float'>,
    'frozenset': <class 'frozenset'>,
    'property': <class 'property'>,
    'int': <class 'int'>,
    'list': <class 'list'>,
    'map': <class 'map'>,
    'object': <class 'object'>,
    'range': <class 'range'>,
    'reversed': <class 'reversed'>,
    'set': <class 'set'>,
    'slice': <class 'slice'>,
    'staticmethod': <class 'staticmethod'>,
    'str': <class 'str'>,
    'super': <class 'super'>,
    'tuple': <class 'tuple'>,
    'type': <class 'type'>,
    'zip': <class 'zip'>,
    '__debug__': True,
    'BaseException': <class 'BaseException'>,
    'Exception': <class 'Exception'>,
    'TypeError': <class 'TypeError'>,
    'StopAsyncIteration': <class 'StopAsyncIteration'>,
    'StopIteration': <class 'StopIteration'>,
    'GeneratorExit': <class 'GeneratorExit'>,
    'SystemExit': <class 'SystemExit'>,
    'KeyboardInterrupt': <class 'KeyboardInterrupt'>,
    'ImportError': <class 'ImportError'>,
    'ModuleNotFoundError': <class 'ModuleNotFoundError'>,
    'OSError': <class 'OSError'>,
    'EnvironmentError': <class 'OSError'>,
    'IOError': <class 'OSError'>,
    'WindowsError': <class 'OSError'>,
    'EOFError': <class 'EOFError'>,
    'RuntimeError': <class 'RuntimeError'>,
    'RecursionError': <class 'RecursionError'>,
    'NotImplementedError': <class 'NotImplementedError'>,
    'NameError': <class 'NameError'>,
    'UnboundLocalError': <class 'UnboundLocalError'>,
    'AttributeError': <class 'AttributeError'>,
    'SyntaxError': <class 'SyntaxError'>,
    'IndentationError': <class 'IndentationError'>,
    'TabError': <class 'TabError'>,
    'LookupError': <class 'LookupError'>,
    'IndexError': <class 'IndexError'>,
    'KeyError': <class 'KeyError'>,
    'ValueError': <class 'ValueError'>,
    'UnicodeError': <class 'UnicodeError'>,
    'UnicodeEncodeError': <class 'UnicodeEncodeError'>,
    'UnicodeDecodeError': <class 'UnicodeDecodeError'>,
    'UnicodeTranslateError': <class 'UnicodeTranslateError'>,
    'AssertionError': <class 'AssertionError'>,
    'ArithmeticError': <class 'ArithmeticError'>,
    'FloatingPointError': <class 'FloatingPointError'>,
    'OverflowError': <class 'OverflowError'>,
    'ZeroDivisionError': <class 'ZeroDivisionError'>,
    'SystemError': <class 'SystemError'>,
    'ReferenceError': <class 'ReferenceError'>,
    'MemoryError': <class 'MemoryError'>,
    'BufferError': <class 'BufferError'>,
    'Warning': <class 'Warning'>,
    'UserWarning': <class 'UserWarning'>,
    'DeprecationWarning': <class 'DeprecationWarning'>,
    'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>,
    'SyntaxWarning': <class 'SyntaxWarning'>,
    'RuntimeWarning': <class 'RuntimeWarning'>,
    'FutureWarning': <class 'FutureWarning'>,
    'ImportWarning': <class 'ImportWarning'>,
    'UnicodeWarning': <class 'UnicodeWarning'>,
    'BytesWarning': <class 'BytesWarning'>,
    'ResourceWarning': <class 'ResourceWarning'>,
    'ConnectionError': <class 'ConnectionError'>,
    'BlockingIOError': <class 'BlockingIOError'>,
    'BrokenPipeError': <class 'BrokenPipeError'>,
    'ChildProcessError': <class 'ChildProcessError'>,
    'ConnectionAbortedError': <class 'ConnectionAbortedError'>,
    'ConnectionRefusedError': <class 'ConnectionRefusedError'>,
    'ConnectionResetError': <class 'ConnectionResetError'>,
    'FileExistsError': <class 'FileExistsError'>,
    'FileNotFoundError': <class 'FileNotFoundError'>,
    'IsADirectoryError': <class 'IsADirectoryError'>,
    'NotADirectoryError': <class 'NotADirectoryError'>,
    'InterruptedError': <class 'InterruptedError'>,
    'PermissionError': <class 'PermissionError'>,
    'ProcessLookupError': <class 'ProcessLookupError'>,
    'TimeoutError': <class 'TimeoutError'>,
    'open': <built-in function open>,
    'quit': Use quit() or Ctrl-Z plus Return to exit,
    'exit': Use exit() or Ctrl-Z plus Return to exit,
    'copyright': Copyright (c) 2001-2018 Python Software Foundation.
    All Rights Reserved.
}

这个字典中包含着python的所有内建功能。我们可以从中找到print()、open()、exit()、False、Bool、dir()等我们常用的内建函数,以及很多异常类。

 

我们通过使用该字典,可以直接队其中的内建函数进行调用:

global_dict = globals()
builtin_dict = global_dict['__builtins__'].__dict__
builtin_dict['print']("Hello Builtin functions")  # 打印 Hello Builtin functions

所以,我们平时在使用内建函数的时候,Python实际上就是在这个字典中查找我们调用的函数名是否存在,存在则调用,不存在则报错。

 

三、元类

我们知道,类可以创建实例对象。从前面我们可以看出,类实际上也是一种对象,那么类是谁的对象呢??答案是元类。

当我们平时在创建类时:

class Test1():
    num1 = 100
    num2 = 200


help(Test1)

通过help我们可以看到:

class Test1(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  num1 = 100
 |  
 |  num2 = 200

 

在Python中,元类就是type类,我们使用type来创建一个普通类:

Test2 = type('Test2', (), {'num1': 100, 'num2': 200})
help(Test2)

通过help可以看到:

class Test2(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  num1 = 100
 |  
 |  num2 = 200

我们可以看到Test1和Test2基本是一样的。我们在程序中通过class关键字创建类,实际上和type()创建一个类,效果一样。

在 Test2 = type('Test2', (), {'num1': 100, 'num2': 200}) 中,第一个参数是类名,第二个参数是父类,第三个参数是属性和方法(用字典列出)。

 

这里注意:我们给一个类取名叫"Test2",那么type的返回值我们也应该用"Test2"来接收(当然也可以用其他的名字),如果不一致的话:

Test222 = type('Test2', (), {'num1': 100, 'num2': 200})
help(Test222)
t = Test222()
# t = Test2()  # 报错

但在help中:

class Test2(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  num1 = 100
 |  
 |  num2 = 200

所以,我们尽量采用一致的名字, 避免出错。

 

如果一个类继承于Test2:

Test3 = type("Test3", (Test2,), {})

 

类中的各种成员方法:

def func1(self):
    print("This is method func1")


@classmethod
def func2(cls):
    print("This is class method func2")


@staticmethod
def func3():
    print("This is static method func3")


Test2 = type('Test2', (), {"func1": func1, "func2": func2, "func3": func3})
t = Test2()
t.func1()
Test2.func2()
t.func3()

 

四、元类什么时候用

元类一般很少使用

我们可以自定义元类,并用于创建普通类:

 

第一种,用函数来修改属性名(在type运行之前):

def upper_attr(class_name, class_parents, class_attr):
    new_attr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    return type(class_name, class_parents, new_attr)


class Foo(object, metaclass=upper_attr):
    bar = 'pig'


print(hasattr(Foo, "bar"))  # 打印 Flase
print(hasattr(Foo, "BAR"))  # 打印 True

f = Foo()
print(f.BAR)   # 打印 pig

upper_attr会修改属性的名称为大写,然后再使用type生成一个Foo类。 

 

第二种,定义一个继承于type的元类来创建Foo类:

class UpperAttrMetaClass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        print("Here is new")
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        return type(class_name, class_parents, new_attr)


class Foo(object, metaclass=UpperAttrMetaClass):
    def __init__(self, abv):
        self.abv = abv
        print("Here is init")

    bar = 'pig'


print(hasattr(Foo, "bar"))  # 打印 Flase
print(hasattr(Foo, "BAR"))  # 打印 True

f = Foo()
print(f.BAR)  # 打印 pig

UpperAttrMetaClass继承于type,则他就变成了一个元类,由于元类在创建Foo类时会调用__new__函数,所以,我们在__new__函数中做了一些额外的操作。

 

五、使用__new__实现单例模式(引申)

既然我们可以在类创建对象之前在__new__中做事情,那么我们可以在__new__中判断这个类是否已经存在实例对象,如果存在则直接返回已存在的对象,如果不存在才创建新的对象,这就实现了单例模式。

# -*- coding: utf-8 -*-
import threading


class Singleton(object):
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._lock:    # 加锁防止多线程环境中两个线程同时判断到上一行代码为True,同时创建该类的实例
                if not hasattr(Singleton, "_instance"):
                    # 调用object类的__new__方法
                    Singleton._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return Singleton._instance


def test(number):
    s = Singleton()
    print str(number) + ": " + str(id(s))


for i in range(10):
    t = threading.Thread(target=test, args=(i, ))
    t.start()
posted @ 2019-12-04 21:33  风间悠香  阅读(279)  评论(0编辑  收藏  举报