为啥要使用 __prepare__ 方法来获取类的命名空间?

     2023-02-23     11

关键词:

【中文标题】为啥要使用 __prepare__ 方法来获取类的命名空间?【英文标题】:Why should I use the __prepare__ method to get a class' namespace?为什么要使用 __prepare__ 方法来获取类的命名空间? 【发布时间】:2017-10-04 21:59:34 【问题描述】:

注意 这个问题与 Python 3 Enum 数据类型无关,这只是我正在使用的示例。

使用PEP 3115,Python 3 将__prepare__1 方法添加到type,以便在创建类时允许使用自定义命名空间。例如,新的Enum 数据类型使用__prepare__ 返回私有_EnumDict 的实例,用作新的Enum 类的命名空间。

但是,我已经看到几个关于 SO2EnumMeta 被子类化的示例,在元类 __new__ 方法中为该类创建一个新的命名空间,而不是调用 __prepare__获取该新命名空间的方法,改为使用type(clsdict)()。这样做有什么风险吗?


1__prepare__的签名:

@classmethod
def __prepare__(metacls, cls, bases, **kwds):

对于__new__

def __new__(metacls, cls, bases, clsdict, **kwds):

2 使用type(clsdict)的示例:

来自this answer

class CountryCodeMeta(enum.EnumMeta):
    def __new__(metacls, cls, bases, classdict):
        data = classdict['data']
        names = [(country['alpha-2'], int(country['country-code'])) for country in data]

  -->   temp = type(classdict)()
        for name, value in names:
            temp[name] = value

        excluded = set(temp) | set(('data',))
        temp.update(item for item in classdict.items() if item[0] not in excluded)

        return super(CountryCodeMeta, metacls).__new__(metacls, cls, bases, temp)

【问题讨论】:

【参考方案1】:

是的,有风险。

通过调用__prepare__ 而不是type(clsdict)() 来获取新命名空间至少有两个原因:

在 Python 2 上运行时,clsdictdict,而原始的 __prepare__ 从未运行过(__prepare__ 仅适用于 Python 3)——换句话说,如果 __prepare__正在返回除普通字典之外的其他内容,type(clsdict)() 不会得到它。

__prepare__clsdict 上设置的任何属性在使用type(clsdict)() 时都不会设置;即如果__prepare__ 确实clsdict.spam = 'eggs' 那么type(clsdict)() 将没有spam 属性。请注意,这些属性位于命名空间本身,供元类使用,在命名空间中不可见。

总结一下:使用__prepare__() 来获取正确的类字典是有充分理由的,而type(clsdict)() 快捷方式则没有。

【讨论】:

为啥 str() 不使用 __getattribute__ 来获取 __str__ 以及如何产生效果?

】为啥str()不使用__getattribute__来获取__str__以及如何产生效果?【英文标题】:Whydoesn\'tstr()use__getattribute__toget__str__andhowtoproducetheeffect?为什么str()不使用__getattribute__来获取__str__以及如何产生效果?【发布时间】:2020-08-0311:04:06【... 查看详情

为啥我不能在工匠命令类的 __construct 方法中访问/使用 $this->option('debug') ?

】为啥我不能在工匠命令类的__construct方法中访问/使用$this->option(\\\'debug\\\')?【英文标题】:Whycan\'tIaccess/use$this->option(\'debug\')inaartisancommandclass\'__constructmethod?为什么我不能在工匠命令类的__construct方法中访问/使用$this->op... 查看详情

为啥元类的 __call__ 方法在类上调用,而原生类的 __call__ 没有?

】为啥元类的__call__方法在类上调用,而原生类的__call__没有?【英文标题】:Whymetaclass\'s__call__methodcalledonclass,butnativeclass\'s__call__not?为什么元类的__call__方法在类上调用,而原生类的__call__没有?【发布时间】:2015-08-3120:44:26【... 查看详情

面对对象的属性和方法

...类的私用属性和私用方法类中私用属性和私用方法的使的使用是加双划线 classA:__N=0#类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__Ndef__init__(self):self.__X=10#变形为self._A__Xdef__foo(sel... 查看详情

新式类__new__()方法

...例化时,__new__()方法用在__init__()启动之前,决定是否要使用该__init__()方法,因为__new__()可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。特性总结:__new__()方法是类在准备实例化时调用,也就是实例化之前... 查看详情

php中__set和__get要怎么理解?

...追问好吧,就是理解差,有些地方不明白参考技术A__get()方法:这个方法用来获取私有成员属性值的,有一个参数,参数传入你要获取的成员属性的名称,返回获取的属性值。如果成员属性不封装成私有的,对象本身就不会去自动... 查看详情

为啥 Python 的 'len' 函数比 __len__ 方法快?

】为啥Python的\\\'len\\\'函数比__len__方法快?【英文标题】:WhyisPython\'s\'len\'functionfasterthanthe__len__method?为什么Python的\'len\'函数比__len__方法快?【发布时间】:2013-12-1615:24:14【问题描述】:在Python中,len是一个函数,通过调用对... 查看详情

类的用法(代码片段)

类与对象的用法一、类的基本使用类中包括:静态字段(静态变量)、动态字段(动态变量)和构造方法静态字段写在__init__方法之前,动态字段需要实例化对象类传递参数值,字段名写在__init__方法中实例化对象的过程:创建一个对... 查看详情

类的内置方法

自定制格式化字符串__format____str__ , __repr__(优先使用,覆盖面广)改变对象的字符串显示__str__,__repr__classA:passa=A()print(a)#<__main__.Aobjectat0x0000000001DD1D68>内存地址自己找不到到父类找l=list()print(l)#[]为什么打出来长上面的不... 查看详情

为啥 Counter 的 __init__ 方法称为描述符?

】为啥Counter的__init__方法称为描述符?【英文标题】:Whyisthe__init__methodofCounterreferredtoasadescriptor?为什么Counter的__init__方法称为描述符?【发布时间】:2016-05-0501:37:43【问题描述】:我在阅读Counter类的__init__方法时,看到了这个... 查看详情

为啥不自动调用超类 __init__ 方法?

】为啥不自动调用超类__init__方法?【英文标题】:Whyaren\'tsuperclass__init__methodsautomaticallyinvoked?为什么不自动调用超类__init__方法?【发布时间】:2011-04-1612:27:06【问题描述】:为什么Python设计者决定子类的__init__()方法不会像在... 查看详情

类的特殊成员

...类是什么看一下输出结果:3、__call__,创建对象的时候是使用:对象=类名();而对于__call__方法的执行是由对象后面加括号来执行的,即:对象()4、__dict__,查看类或对象中的所有成员打印效果:输出类中的所有属性、方法,包... 查看详情

使用类的 __new__ 方法作为工厂:__init__ 被调用两次

】使用类的__new__方法作为工厂:__init__被调用两次【英文标题】:Usingaclass\'__new__methodasaFactory:__init__getscalledtwice【发布时间】:2011-08-2215:56:19【问题描述】:我在python中遇到了一个奇怪的错误,使用类的__new__方法作为工厂会导... 查看详情

python中类的专用方法(代码片段)

...,在生成对象时调用__del__:析构函数,释放对象时使用__repr__:打印,转换__setitem__:按照索引赋值__getitem__:按照索引获取值__len__:获得长度__cmp__:比较运算__call__:函数调用__add__:加运算__sub__:减运算__mul__:乘运算__truediv__:除... 查看详情

类的私有变量和私有方法(代码片段)

...类调用如果希望通过对象直接调用私有变量和方法,可以使用**obj._类名__变量名/方法名**来调用,但是这种方法是python3的一个bug,工作中不能使用。classPerson(object):skin_color='黄色'__num='one'#__变量名表示的是私有的静... 查看详情

常见的魔法方法及使用

常见的魔法方法有如下:__init__初始化类属性的方法__repr__定义当repr() 被你的一个类的实例调用时所要产生的行为。str()和repr()的主要区别是其目标群体。repr()返回的是机器可读的输出,而str()返回的是人类可读的。__str__定义... 查看详情

如何在 python 2 中为元类替换 __prepare__

】如何在python2中为元类替换__prepare__【英文标题】:Howtoreplace__prepare__formetaclassinpython2【发布时间】:2017-05-0421:57:16【问题描述】:我需要将一些代码从python3转换为python2。我有一个元类,其中__prepare__方法在类dict中设置了一个... 查看详情

为啥在 Python 3 中实例的 __dict__ 的大小要小得多?

】为啥在Python3中实例的__dict__的大小要小得多?【英文标题】:Whyisthe__dict__ofinstancessomuchsmallerinsizeinPython3?为什么在Python3中实例的__dict__的大小要小得多?【发布时间】:2017-07-1403:02:44【问题描述】:在Python中,为类的实例创建... 查看详情