【问题标题】:How to create a static class property that returns an instance of the class itself?如何创建一个返回类本身实例的静态类属性?
【发布时间】:2019-06-28 10:53:48
【问题描述】:

我编写了一个可以处理任意精度整数的类(仅用于学习目的)。该类采用整数的字符串表示并将其转换为BigInt 的实例以供进一步计算。

您经常需要数字 ,所以我认为如果班级可以返回这些数字会很有帮助。我尝试了以下方法:

class BigInt():
    zero = BigInt("0")

    def __init__(self, value):
        ####yada-yada####

这不起作用。错误:“名称 'BigInt' 未定义”

然后我尝试了以下方法:

class BigInt():

    __zero = None

    @staticmethod
    def zero():
        if BigInt.__zero is None:
            BigInt.__zero = BigInt('0')
        return BigInt.__zero


    def __init__(self, value):
        ####yada-yada####

这实际上非常有效。我不喜欢zero 是一种违反直觉的方法(因此必须使用BigInt.zero() 调用),因为它应该只引用一个固定值。

所以我尝试将zero 更改为一个属性,但随后编写BigInt.zero 返回类property 而不是BigInt 的实例,因为使用了装饰器。由于类型错误,该实例无法用于计算。

有没有办法解决这个问题?

【问题讨论】:

  • 你不能创建一个继承那个类的类。从而实例化您的子类,它可以返回带有"0" 作为值的父类的活动实例? class initator(BigInt): def __init__(self): super(initator, self).__init__("8").

标签: python oop properties


【解决方案1】:

静态属性...?我们称静态属性为“属性”。这不是 Java,Python 是一种动态类型的语言,这样的结构真的会使事情变得过于复杂。

只需这样做,设置一个类属性:

class BigInt: 
    def __init__(self, value): 
        ... 

BigInt.zero = BigInt("0")

如果您希望它完全在类中定义,请使用装饰器(但请注意,它只是编写相同内容的一种更花哨的方式)。

def add_zero(cls):
    cls.zero = cls("0")
    return cls

@add_zero
class BigInt:
    ...

【讨论】:

  • 我一般同意,staticproperty 的组合只是一个固定值的属性。然而,在类级别具有真正的@property 属性(描述符协议属性)的更普遍的问题是有趣的,并且应该通过将这样的描述符对象分配给元类来完成。 (不过,我还没有对此进行测试。它也使它只能从类级别访问,而不是实例级别。)
  • 谢谢!不认为这是 this 显而易见的,但是来自其他语言的 python 方法有时很难理解。你能解释一下它如何与装饰器一起工作吗?我尝试了以下导致递归深度错误的方法:def big_int_decorator(cl): def wrapper(*args): value = cl(*args) value.z = BigInt("0") return value return wrapper
  • @wim 非常感谢!
【解决方案2】:

问题是矛盾的:staticproperty 不要这样在一起。 Python 中的静态属性 只是被赋值一次的简单属性,而语言本身包含了非常多的这些属性。 (大多数字符串是隐藏的,所有小于某个值的整数都是预先构造的,等等。例如the string module。)。最简单的方法是在构造后静态分配属性,如 wim 所示:

class Foo:
    ...

Foo.first = Foo()
...

或者,正如他进一步建议的那样,使用类装饰器来执行分配,这在功能上与上述相同。装饰器实际上是一个以“装饰”函数作为参数的函数,并且必须返回一个函数以有效地替换原始函数。这可能是原始函数,例如,用一些注释修改,或者可能是完全不同的函数。原始(装饰)函数可能会或可能不会被适当地调用为装饰器。

def preload(**values):
    def inner(cls):
        for k, v in values.items():
            setattr(cls, k, cls(v))

        return cls

    return inner

这可以动态使用:

@preload(zero=0, one=1)
class Foo:
    ...

如果目的是在常见整数值上节省一些时间,defaultdict 将整数映射到构造的BigInts 可以用作缓存和流线型构造/单例存储的一种形式。 (例如BigInt.numbers[27]

但是,在课堂级别使用@property 的问题引起了我的兴趣,因此我进行了一些挖掘。如果将属性向上推到对象模型层次结构中,则完全可以在类级别使用“descriptor protocol objects”(@property 装饰器返回)到元类。

class Foo(type):
    @property
    def bar(cls):
        print("I'm a", cls)
        return 27

class Bar(metaclass=Foo):
    ...

>>> Bar.bar
I'm a <class '__main__.Bar'>
<<< 27

值得注意的是,该属性不能从实例中访问:

>>> Bar().bar
AttributeError: 'Bar' object has no attribute 'bar'

希望这会有所帮助!

【讨论】:

  • 您用元类的东西描述的更像是类方法的属性,而不是静态方法的属性。这种做法和所有相关的陷阱都在这里详细讨论:Using property() on classmethods
  • @wim 该问题有 14 个答案,最高答案提到 Python 2.2(超越古代)并且不提供完整功能(没有设置器/删除器),第二多投票的答案 确实 提到元类,并没有指出通过实例访问的重要问题,一些使用@property w/ 从实例访问,这与我在这里的目标明确相反,等等。是否有具体的可以指出的注意事项? (虽然是的,这在技术上更像是一个属性classmethod 而不是staticmethod。)
  • currently top-voted 的答案被打破了,原因在 cmets 中有讨论。这个2016 answer 看起来不错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多