鱼C论坛

 找回密码
 立即注册
查看: 1919|回复: 6

元类的单例模式,有个问题向大家请教下。

[复制链接]
发表于 2019-12-3 02:52:36 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
class Singleton(type):
    def __call__(cls, *args, **kwargs):
        print(cls, args, kwargs)
        if not hasattr(cls, '_instance'):
            print('cls in it')
            # 问题是这个super(Singleton, cls)到底是什么?为什么没有
            # __call__的属性,我调用__call__又可以,实在然人费解

            print(dir(super(Singleton, cls)))
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
            # cls._instance.__init__(*args, **kwargs)
            print('cls is over __init__')
        return cls._instance


class Foo(metaclass=Singleton):
    def __init__(self, name):
        print('Foo__init__')
        self.x = 1
        self.name = name
        print('foo__init__out')

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)


f1 = Foo('sidian')
f2 = Foo('wudian')
print(f1 == f2)
print(f1.x)
print(f1.name)
print(f2.name)


输出

<class '__main__.Foo'> ('sidian',) {}
cls in it
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__']
Foo__init__
foo__init__out
cls is over __init__
<class '__main__.Foo'> ('wudian',) {}
True
1
sidian
sidian
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2019-12-3 02:54:59 | 显示全部楼层
问题是前面我下划线的地方,那个super(Singleton, cls)到底指的是什么?super到现在比我想象中的理解要累。
为什么这个super(Singleton, cls)在dir的时候没有__call__属性,但cls._instance = super(Singleton, cls).__call__(*args, **kwargs)却又可以执行。

实在太让人费解了。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-12-3 04:25:36 | 显示全部楼层
本帖最后由 Stubborn 于 2019-12-3 05:13 编辑

回答问题,call是有的:
  1. print(Singleton.__dict__)
  2. {'__module__': '__main__', '__call__': <function Singleton.__call__ at 0x10b8849e0>, '__doc__': None}
复制代码


首先需要理解,什么叫元类??

在Python中什么都叫对象,比如你定义一个class 进行实例化,可以得到一个类,比如Python的自带的list,str类,既然他们都是类,那么他们是由谁来创建的

答案是 type

那么问题又来了,type既然是一个类,它又是由谁来创建的?

答案还是type

并且他们都有统一的父类 object

  1. MacdeiMac:~ mac$ python3
  2. Python 3.7.5 (default, Nov  1 2019, 02:16:38)
  3. [Clang 10.0.0 (clang-1000.11.45.5)] on darwin
  4. Type "help", "copyright", "credits" or "license" for more information.
  5. >>> type(list)
  6. <class 'type'>
  7. >>> list.__bases__
  8. (<class 'object'>,)
  9. >>> type(type)
  10. <class 'type'>
  11. >>> type.__bases__
  12. (<class 'object'>,)
  13. >>> object.__bases__
  14. ()
  15. >>>
复制代码


回到上面,上面元类,元类就是用来控制类的实例化。举个简单的例子,在Django的数据模型中,经常可以用到这样的代码。具体怎么使用元类去进行实现,可以参考我的这个帖子:元类编程(new的应用)

  1. class User(BaseMode):

  2.     name = CharField(db_column="name", max_length=10)
  3.     age = IntField(db_column="age", min_value=10, max_value=100)

  4.     class Meta:
  5.         db_table = "user"

  6. if __name__ == '__main__':
  7.     user = User()
  8.     user.name = "Alex"
  9.     user.age = 26
  10.     user.save()
复制代码



在看你的代码
  1. class Singleton(type):
  2.     def __call__(cls, *args, **kwargs):
  3.         if not hasattr(cls, '_instance'):
  4.             cls.instance = super().__call__(*args, **kwargs)

  5.         return cls.instance


  6. class Foo(metaclass=Singleton):
  7.     def __init__(self, name):
  8.         self.x = 1
  9.         self.name = name

  10.     def __new__(cls, *args, **kwargs):
  11.         return object.__new__(cls)


  12. # print(Foo.instance)
  13. f1 = Foo('sidian')
  14. print(Foo.instance)
  15. f2 = Foo('wudian')
  16. print(f1 == f2)
  17. print(f1.x)
  18. print(f1.name)
  19. print(f2.name)
复制代码


在第一次,实例f1时候,步骤怎么走的?它先到元类Singleton 里面看下,这里只定义了一个call,它就到里面了,问下里面有没有 _instance 属性啊,没有啊,那我创建一个吧。

此时还没有进入到 return !  既然要创建,它创建的是什么,是 Foo 的实例化对象 。

我学习的也不是很精。可能讲的不是很明白,说白了,元类可以在类实例化的时候,往类里面加点”料“, 还是不懂等大神继续回复。额外附送一段,学习笔记代码

  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/python3
  3. """
  4. @ version: ??
  5. @ author: Alex
  6. @ file: metaclass_test
  7. @datetime: 上午2:46
  8. @explain:元类对象
  9. """


  10. # 类也是对象, type是用来创建类的类

  11. # 创建动态类
  12. def create_class(name):
  13.     if name == "user":
  14.         class User:
  15.             def __str__(self):
  16.                 return "user"

  17.         return User
  18.     elif name == "company":
  19.         class Company:
  20.             def __str__(self):
  21.                 return "company"

  22.         return Company


  23. def say(self):
  24.     return "i am user"


  25. class BaseClass:

  26.     def answer(self):
  27.         return "i am baseclass"


  28. """
  29. 什么是元类?元类是创建类的类,对象是由<-class(创建)<-type(创建)
  30. """


  31. class MetaClass(type):

  32.     def __new__(cls, *args, **kwargs):
  33.         return super().__new__(cls, *args, **kwargs)


  34. class User(metaclass=MetaClass):
  35.     """metaclass 指定User交给谁来实例化,通过metaclass来创建user类"""

  36.     def __init__(self, name):
  37.         self.name = name

  38.     def __str__(self):
  39.         return self.name

  40.     def parse_one(self):
  41.         ...

  42.     def parse_two(self):
  43.         ...

  44.     def parse_three(self):
  45.         ...


  46. if __name__ == '__main__':
  47.     # type创建动态类
  48.     User = type("user", (BaseClass,), {"name": "user", "say": say})
  49.     obj = User()
  50.     print(obj.say())
  51.     print(obj.answer())
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-12-3 05:09:12 | 显示全部楼层
123.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-12-3 17:05:34 | 显示全部楼层
Stubborn 发表于 2019-12-3 04:25
回答问题,call是有的:

class User(BaseMode):

    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=10, max_value=100)

    class Meta:
        db_table = "user"

Django里面定义模型,里面这个class我还不是很清楚具体做了什么?外面这个我看了廖雪峰的笔记,
是通过继承Model继承的原类创建对象,但里面的这个class Meta具体操作,我还真要好好想想
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-12-3 20:25:14 | 显示全部楼层
四点好 发表于 2019-12-3 17:05
class User(BaseMode):

    name = CharField(db_column="name", max_length=10)

这个META没什么可说的,你就当做是一个固定的写法。就是这个类继承的models.Model类里面就定义了一个META的类,用来存储一些默认的配置,你自己写一遍,相当于自己重写了这个META类,也就是用来改变一些他默认的配置。
META里面的配置不多就几个而已,像联合索引,联合唯一索引,默认排序,另起表名,ADMIN里这个model的名字之类的。
有需要就重写,没有需要就当做这个META不存在。
说白了,就是一个用来存放配置信息的东西。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-12-4 15:20:08 | 显示全部楼层
天圆突破 发表于 2019-12-3 20:25
这个META没什么可说的,你就当做是一个固定的写法。就是这个类继承的models.Model类里面就定义了一个META ...

我测试过了,Meta信息只不过是这个类属性的一部分,最后会传入到Metaclass元类里面塞选信息,原理不复杂。是我想复杂了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-1-21 03:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表