[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()
[Python之路] 元类(引申 单例模式)的更多相关文章
- [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式
使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...
- 深刻理解Python中的元类(metaclass)以及元类实现单例模式
在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍 ...
- Python元类(metaclass)以及元类实现单例模式
这里将一篇写的非常好的文章基本照搬过来吧,这是一篇在Stack overflow上很热的帖子,我看http://blog.jobbole.com/21351/这篇博客对其进行了翻译. 一.理解类也是对 ...
- python基础——使用元类
python基础——使用元类 type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的. 比方说我们要定义一个Hello的class,就写一个hello. ...
- Python基础:元类
一.概述 二.经典阐述 三.核心总结 1.类的创建过程 2.元类的使用惯例 四.简单案例 1.默认行为 2.使用元类 五.实践为王 一.概述 Python虽然是多范式的编程语言,但它的数据模型却是 纯 ...
- Python中的元类(metaclass)
推荐+收藏:深刻理解Python中的元类(metaclass) 做一些笔记学习学习: 在大多数编程语言中,类就是用来描述如何生成一个对象的代码段,在Python中类也是一个对象,这个(类)对象自身拥有 ...
- 什么是python中的元类
所属网站分类: python高级 > 面向对象 作者:goodbody 原文链接: http://www.pythonheidong.com/blog/article/11/ 来源:python ...
- Python之面向对象元类
Python之面向对象元类 call方法: class People: def __init__(self,name): self.name=name # def __call__(self, *ar ...
- Python 中的元类到底是什么?这篇恐怕是最清楚的了
类作为对象 在理解元类之前,您需要掌握 Python 的类.Python 从 Smalltalk 语言中借用了一个非常特殊的类概念. 在大多数语言中,类只是描述如何产生对象的代码段.在 Python ...
随机推荐
- Jenkins+SVN持续环境搭建
需要三台不同环境的服务器,SVN.Jenkins.Tomcat 1.SVN搭建 1.Subversion服务器(SVN服务器) 2.项目对应的版本库 3.版本库中钩子程序(用于触发构建命令) 在我以前 ...
- vim 常用基本
vim 基本操作 0. 基本操作 :w // 保存当前文件 :q // 退出vim :wq // 保存退出 :w! // 强制保存当前文件 :q! // 强制退出(可以忽略修改) :!cmd // 执 ...
- php用逗号格式化数字
今日工作需要格式化数字显示当前商品价格,比如2335.32,需要格式化为2,335.32这样显示.我写了一个函数.总感觉这么简单的功能,但是却需要30多行代码来完成. <?php/**** * ...
- thinkPHP连接数据库报错:PDOException in Connection.php line 295
跑去网上找了N多方法来尝试,重装apache.mysql.安装集成软件都试过了.错误一样. 后来细细分析,PDOException in Connection指的不就是PDO异常吗? 然后去了解了一些 ...
- ASP.NET练习③——AspNetChosmePager
aspx代码: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="_Chosm ...
- dev chart使用
public class LineChartHelp { #region 折线图 /// <summary> /// 创建折线图 /// </summary> public v ...
- Http请求头缓存设置方法
1.直接在.aspx页面中设置最直接的,在.aspx页面中添加一行如下代码: <%@ OutputCache Duration="3600" VaryByParam=&quo ...
- js判断一个 object 对象是否为空
方法一:使用for...in for...in... 遍历属性,为真则为“非空数组”:否则为“空数组” for (var i in obj) { return true // 如果不为空,则会执行到 ...
- Oracle数据库中的变量
Oracle数据库中的变量 来源:https://blog.csdn.net/wahaa591/article/details/46772769 1.define(即host变量) define va ...
- 如何为 esp32 编译和配置及烧写 MicroPython 固件。
MicroPython 在 esp-idf (esp32) 上编译固件 esp32 编译 micropython 的固件相关的资料应该很多吧,我也会出一篇,但会额外讲一些 linux 的东西的. 资料 ...