Assuming you don't need actual class attributes (you're always constructing an instance of Foo
to use the attribute anyway, and it's not common for class attributes to be both public and logically mutable), the correct solution is to make x
a property
that wraps an instance attribute, which has independent values per instance, established in the __init__
initializer for the class:
class Foo:
def __init__(self):
self._x = 0 # _x is a protected instance attr that the property uses behind the scenes
@property
def x(self): # getter for x is normal
return self._x
@x.setter
def x(self, newx): # setter for x massages the value before setting it
if newx >= 0: # Cheaper to handle 0 on same code path as > 0 so you needn't test < 0
newx %= 100
else:
newx = -1
self._x = newx
Usage is pretty simple:
>>> myfoo = Foo()
>>> myfoo.x = 1983
>>> myfoo.x
83
>>> myfoo.x = -3748972983
>>> myfoo.x
-1
In case it really needs to be a class attribute and it must be accessible on instances, the solution gets ugly, as you need a metaclass to provide property
s on classes, and additional properties on the class itself to delegate access on instances to the class itself.
Note: I strongly discourage actually doing this as anything other than an exercise:
class FooMeta(type): # Inheriting from type makes metaclass
@property
def x(cls):
return cls._x
@x.setter
def x(cls, newx):
if newx >= 0:
newx %= 100
else:
newx = -1
cls._x = newx
class Foo(metaclass=FooMeta):
_x = 0
# Must make properties on class if instances are to "benefit" from class property
@property
def x(self):
return type(self).x
@x.setter
def x(self, newx):
type(self).x = newx
That allows the following to work:
>>> Foo.x # x exists on Foo itself, not just instances
>>> Foo.x = 1983
>>> Foo.x
83
>>> f = Foo()
>>> f.x # Accessible on instances too
83
>>> f.x = -234789
>>> f.x # Same behavior on instance
-1
>>> Foo.x # Changing instance changed class
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…