猿问

静态类变量是否可能?

静态类变量是否可能?

是否有可能在python中有静态类变量或方法?这样做需要什么语法?



慕斯王
浏览 654回答 4
4回答

扬帆大鱼

在类定义中声明但在方法内部未声明的变量是类或静态变量:>>> class MyClass:...     i = 3...>>> MyClass.i3正如@millerdev所指出的,这会创建一个类级i变量,但这与任何实例级i变量都不同,所以你可以拥有>>> m = MyClass()>>> m.i = 4>>> MyClass.i, m.i>>> (3, 4)这与C ++和Java不同,但与C#没有什么不同,在C#中,使用对实例的引用无法访问静态成员。了解Python教程对类和类对象主题的看法。@Steve Johnson已经回答了有关静态方法的问题,也在Python Library Reference的“内置函数”中进行了介绍。class C:     @staticmethod     def f(arg1, arg2, ...): ...@beidy建议使用类方法而不是static 方法,因为该方法接收类类型作为第一个参数,但我对这种方法相对于静态方法的优势仍然有点模糊。如果你也是,那么它可能并不重要。

蛊毒传说

@Blair Conrad说在类定义中声明的静态变量,但不在方法内部是类或“静态”变量:>>> class Test(object):...     i = 3...>>> Test.i3这里有一些问题。继续上面的例子:>>> t = Test()>>> t.i     # "static" variable accessed via instance3>>> t.i = 5 # but if we assign to the instance ...>>> Test.i  # we have not changed the "static" variable3>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i5>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class>>> t.i5>>> Test.i6>>> u = Test()>>> u.i6           # changes to t do not affect new instances of Test# Namespaces are one honking great idea -- let's do more of those!>>> Test.__dict__{'i': 6, ...}>>> t.__dict__{'i': 5}>>> u.__dict__{}注意直接设置t.i属性时实例变量与“static”类变量的同步。这是因为在命名空间内重新绑定,这与命名空间不同。如果要更改“静态”变量的值,则必须在最初定义它的范围(或对象)内更改它。我把“静态”放在引号中,因为Python在C ++和Java的意义上并没有真正的静态变量。ititTest虽然它没有说明有关静态变量或方法的任何内容,但Python教程提供了有关类和类对象的一些相关信息。@Steve Johnson也回答了有关静态方法的问题,也在Python Library Reference的“内置函数”中进行了介绍。class Test(object):    @staticmethod    def f(arg1, arg2, ...):        ...@beid还提到了classmethod,类似于staticmethod。classmethod的第一个参数是类对象。例:class Test(object):    i = 3 # class (or static) variable    @classmethod    def g(cls, arg):        # here we can use 'cls' instead of the class name (Test)        if arg > cls.i:            cls.i = arg # would the the same as  Test.i = arg1

MM们

静态和类方法正如其他答案所指出的那样,使用内置装饰器可以轻松完成静态和类方法:class Test(object):     # regular instance method:     def MyMethod(self):         pass     # class method:     @classmethod     def MyClassMethod(klass):         pass     # static method:     @staticmethod     def MyStaticMethod():         pass像往常一样,第一个参数MyMethod()绑定到类实例对象。与此相反,第一个参数MyClassMethod()被绑定到类对象本身(例如,在这种情况下,Test)。因为MyStaticMethod(),没有任何参数被绑定,并且具有参数是可选的。“静态变量”然而,实现“静态变量”(好吧,可变的静态变量,无论如何,如果这不是一个矛盾......)并不是那么简单。正如米勒德夫在他的回答中指出的那样,问题在于Python的类属性并不是真正的“静态变量”。考虑:class Test(object):     i = 3  # This is a class attributex = Test()x.i = 12   # Attempt to change the value of the      class attribute using x instanceassert x.i == Test.i  # ERRORassert Test.i == 3    # Test.i was not affectedassert x.i == 12          # x.i is a different object than Test.i这是因为该行x.i = 12添加了一个新的实例属性i,x而不是更改Testclass i属性的值。部分预期的静态变量行为,即多个实例之间的属性同步(但不与类本身同步;请参阅下面的“gotcha”),可以通过将class属性转换为属性来实现:class Test(object):     _i = 3     @property     def i(self):         return type(self)._i    @i.setter    def i(self,val):         type(self)._i = val## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE #### (except with separate methods for          getting and setting i) ##class Test(object):     _i = 3     def get_i(self):         return type(self)._i    def set_i(self,val):         type(self)._i = val     i = property(get_i, set_i)现在你可以这样做:x1 = Test()x2 = Test()x1.i = 50assert x2.i == x1.i  # no errorassert x2.i == 50    # the property is synced现在,静态变量将在所有类实例之间保持同步。(注意:也就是说,除非一个类实例决定定义它自己的版本_i!但如果某人决定这样做,他们应该得到他们得到的,不是吗?)请注意,从技术上讲,i它仍然不是一个“静态变量”; 它是一个property,它是一种特殊类型的描述符。但是,该property行为现在等同于在所有类实例中同步的(可变)静态变量。不可变的“静态变量”对于不可变的静态变量行为,只需省略propertysetter:class Test(object):     _i = 3     @property     def i(self):         return type(self)._i## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE #### (except with separate methods for          getting i) ##class Test(object):     _i = 3     def get_i(self):         return type(self)._i     i = property(get_i)现在尝试设置实例i属性将返回AttributeError:x = Test()assert x.i == 3  # successx.i = 12         # ERROR一个要意识到的需要注意的是,上述方法只适用于工作的情况下,你的类-他们不工作,使用类本身时。例如:x = Test()assert x.i == Test.i  # ERROR# x.i and Test.i are two different objects:type(Test.i)  # class 'property'type(x.i)     # class 'int'行assert Test.i == x.i产生一个错误,这是因为i的属性Test和x是两个不同的对象。很多人会发现这令人惊讶。但是,它不应该。如果我们返回并检查我们的Test类定义(第二个版本),我们会注意到这一行:    i = property(get_i)显然,部件i的Test必须是一个property对象,该对象是对象的从返回的类型property的功能。如果您发现上述情况令人困惑,您很可能仍会从其他语言(例如Java或c ++)的角度考虑它。您应该研究property对象,返回Python属性的返回顺序,描述符协议和方法解析顺序(MRO)。我提出了以下'gotcha'的解决方案; 但是我会建议 - 强烈地 - 你不要尝试做以下事情,直到 - 至少 - 你彻底明白为什么assert Test.i = x.i会导致错误。REAL,ACTUAL静态变量 -Test.i == x.i我在下面提供(Python 3)解决方案仅供参考。我并不赞同它是一个“好的解决方案”。我怀疑是否真的需要在Python中模拟其他语言的静态变量行为。但是,无论它是否真的有用,下面应该有助于进一步理解Python的工作原理。更新:这次尝试非常糟糕 ; 如果你坚持做这样的事情(提示:请不要; Python是一种非常优雅的语言,并且只是不需要像其他语言一样表现出来),请使用Ethan Furman的答案中的代码。使用元类模拟其他语言的静态变量行为元类是类的类。Python中所有类的默认元类(即我认为的Python 2.3之后的“新风格”类)type。例如:type(int)  # class 'type'type(str)  # class 'type'class Test(): passtype(Test) # class 'type'但是,您可以像这样定义自己的元类:class MyMeta(type): pass并将其应用到您自己的类中(仅限Python 3):class MyClass(metaclass = MyMeta):     passtype(MyClass)  # class MyMeta下面是我创建的元类,它试图模仿其他语言的“静态变量”行为。它基本上可以通过用版本替换默认的getter,setter和deleter来工作,这些版本检查所请求的属性是否是“静态变量”。“静态变量”的目录存储在StaticVarMeta.statics属性中。最初尝试使用替代分辨率顺序来解析所有属性请求。我把它称为“静态分辨率顺序”或“SRO”。这是通过在给定类(或其父类)的“静态变量”集中查找所请求的属性来完成的。如果该属性未出现在“SRO”中,则该类将回退到默认属性get / set / delete行为(即“MRO”)。from functools import wrapsclass StaticVarsMeta(type):     '''A metaclass for creating classes that emulate the "static variable" behavior     of other languages. I do not advise actually using this for anything!!!     Behavior is intended to be similar to classes that use __slots__. However, "normal"     attributes and __statics___ can coexist (unlike with __slots__).      Example usage:          class MyBaseClass(metaclass = StaticVarsMeta):             __statics__ = {'a','b','c'}             i = 0  # regular attribute             a = 1  # static var defined (optional)         class MyParentClass(MyBaseClass):             __statics__ = {'d','e','f'}             j = 2              # regular attribute             d, e, f = 3, 4, 5  # Static vars             a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)         class MyChildClass(MyParentClass):             __statics__ = {'a','b','c'}             j = 2  # regular attribute (redefines j from MyParentClass)             d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)             a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''     statics = {}     def __new__(mcls, name, bases, namespace):         # Get the class object         cls = super().__new__(mcls, name, bases, namespace)         # Establish the "statics resolution order"         cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))         # Replace class getter, setter, and deleter for instance attributes         cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)         cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)         cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)         # Store the list of static variables for the class object         # This list is permanent and cannot be changed, similar to __slots__         try:             mcls.statics[cls] = getattr(cls,'__statics__')         except AttributeError:             mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided         # Check and make sure the statics var names are strings         if any(not isinstance(static,str) for static in mcls.statics[cls]):             typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__                         raise TypeError('__statics__ items must be strings, not {0}'.format(typ))         # Move any previously existing, not overridden statics to the static var parent class(es)         if len(cls.__sro__) > 1:             for attr,value in namespace.items():                 if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:                     for c in cls.__sro__[1:]:                         if attr in StaticVarsMeta.statics[c]:                             setattr(c,attr,value)                             delattr(cls,attr)         return cls    def __inst_getattribute__(self, orig_getattribute):         '''Replaces the class __getattribute__'''         @wraps(orig_getattribute)         def wrapper(self, attr):             if StaticVarsMeta.is_static(type(self),attr):                 return StaticVarsMeta.__getstatic__(type(self),attr)             else:                 return orig_getattribute(self, attr)         return wrapper    def __inst_setattr__(self, orig_setattribute):         '''Replaces the class __setattr__'''         @wraps(orig_setattribute)         def wrapper(self, attr, value):             if StaticVarsMeta.is_static(type(self),attr):                 StaticVarsMeta.__setstatic__(type(self),attr, value)             else:                 orig_setattribute(self, attr, value)         return wrapper    def __inst_delattr__(self, orig_delattribute):         '''Replaces the class __delattr__'''         @wraps(orig_delattribute)         def wrapper(self, attr):             if StaticVarsMeta.is_static(type(self),attr):                 StaticVarsMeta.__delstatic__(type(self),attr)             else:                 orig_delattribute(self, attr)         return wrapper    def __getstatic__(cls,attr):         '''Static variable getter'''         for c in cls.__sro__:             if attr in StaticVarsMeta.statics[c]:                 try:                     return getattr(c,attr)                 except AttributeError:                     pass         raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))     def __setstatic__(cls,attr,value):         '''Static variable setter'''         for c in cls.__sro__:             if attr in StaticVarsMeta.statics[c]:                 setattr(c,attr,value)                 break     def __delstatic__(cls,attr):         '''Static variable deleter'''         for c in cls.__sro__:             if attr in StaticVarsMeta.statics[c]:                 try:                     delattr(c,attr)                     break                 except AttributeError:                     pass         raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))     def __delattr__(cls,attr):         '''Prevent __sro__ attribute from deletion'''         if attr == '__sro__':             raise AttributeError('readonly attribute')         super().__delattr__(attr)     def is_static(cls,attr):         '''Returns True if an attribute is a static variable of any class in the __sro__'''         if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):             return True         return False
随时随地看视频慕课网APP

相关分类

Python
我要回答