class MyClass:
def myInstanceMethod(self):print 'output from an instance method'
@classmethoddef myStaticMethod(cls):print 'output from a static method'
>>> MyClass.myInstanceMethod()Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: unbound method myInstanceMethod() must be called [...]
>>> MyClass.myStaticMethod()output from a static method
>>> 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__{}
class Test(object):@staticmethoddef f(arg1, arg2, ...):...
@beid还提到了类方法,它类似于静态方法。类方法的第一个参数是类对象。例子:
class Test(object):i = 3 # class (or static) variable@classmethoddef g(cls, arg):# here we can use 'cls' instead of the class name (Test)if arg > cls.i:cls.i = arg # would be the same as Test.i = arg1
>>> class X:... pass...>>> X.bar = 0>>> x = X()>>> x.bar0>>> x.fooTraceback (most recent call last):File "<interactive input>", line 1, in <module>AttributeError: X instance has no attribute 'foo'>>> X.foo = 1>>> x.foo1
class StaticClassError(Exception):pass
class StaticClass:__metaclass__ = abc.ABCMeta
def __new__(cls, *args, **kw):raise StaticClassError("%s is a static class and cannot be initiated."% cls)
class MyClass(StaticClass):a = 1b = 3
@staticmethoddef add(x, y):return x+y
class ConstantAttribute(object):'''You can initialize my value but not change it.'''def __init__(self, value):self.value = value
def __get__(self, obj, type=None):return self.value
def __set__(self, obj, val):pass
class Demo(object):x = ConstantAttribute(10)
class SubDemo(Demo):x = 10
demo = Demo()subdemo = SubDemo()# should not changedemo.x = 100# should changesubdemo.x = 100print "small demo", demo.xprint "small subdemo", subdemo.xprint "big demo", Demo.xprint "big subdemo", SubDemo.x
导致…
small demo 10small subdemo 100big demo 10big subdemo 10
class Test(object):i = 3 # This is a class attribute
x = 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
class Test(object):
_i = 3
@propertydef i(self):return type(self)._i
@i.setterdef 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
class Test(object):
_i = 3
@propertydef 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)
from functools import wraps
class StaticVarsMeta(type):'''A metaclass for creating classes that emulate the "static variable" behaviorof 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 attributea = 1 # static var defined (optional)
class MyParentClass(MyBaseClass):__statics__ = {'d','e','f'}j = 2 # regular attributed, e, f = 3, 4, 5 # Static varsa, 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 objectcls = 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 attributescls.__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 stringsif 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 clsdef __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 wrapperdef __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 wrapperdef __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 wrapperdef __getstatic__(cls,attr):'''Static variable getter'''for c in cls.__sro__:if attr in StaticVarsMeta.statics[c]:try:return getattr(c,attr)except AttributeError:passraise 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)breakdef __delstatic__(cls,attr):'''Static variable deleter'''for c in cls.__sro__:if attr in StaticVarsMeta.statics[c]:try:delattr(c,attr)breakexcept AttributeError:passraise 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 Truereturn False
class Fud:
class_vars = {'origin_open':False}
def __init__(self, origin = True):self.origin = originself.opened = Trueif origin:self.class_vars['origin_open'] = True
def make_another_fud(self):''' Generating another Fud() from the origin instance '''
return Fud(False)
def close(self):self.opened = Falseif self.origin:self.class_vars['origin_open'] = False
fud1 = Fud()fud2 = fud1.make_another_fud()
print (f"is this the original fud: {fud2.origin}")print (f"is the original fud open: {fud2.class_vars['origin_open']}")# is this the original fud: False# is the original fud open: True
fud1.close()
print (f"is the original fud open: {fud2.class_vars['origin_open']}")# is the original fud open: False
class Calculator:@staticmethoddef multiply(n1, n2, *args):Res = 1for num in args: Res *= numreturn n1 * n2 * Res
print(Calculator.multiply(1, 2, 3, 4)) # 24
from contextlib import contextmanager
class Sheldon(object):foo = 73
def __init__(self, n):self.n = n
def times(self):cls = self.__class__return cls.foo * self.n#self.foo * self.n would give the same result here but is less readable# it will also create a local variable which will make it easier to break your code
def updatefoo(self):cls = self.__class__cls.foo *= self.n#self.foo *= self.n will not work here# assignment will try to create a instance variable foo
@classmethod@contextmanagerdef reset_after_test(cls):originalfoo = cls.fooyieldcls.foo = originalfoo#if you don't do this then running a full test suite will fail#updates to foo in one test will be kept for later tests