问题
Does Python have a mechanism for class constructors, i.e. a function that is called whenever the class is first referenced (as opposed to when an instance of that object is created)? I know this exists in some other languages, but I haven't come across it in Python.
Basically, I would like to initialise some static attributes in that function. I put an example below of what I would expect. Of course, the example returns None, but I would like it return 'foo'.
class T:
arg = None
def __class_constructor__():
T.arg = 'foo'
print(T.arg) # returns None
To avoid confusion: I am well aware of the object constructor, but that's not what I want, because it is only called once the first object is created, not before:
class T:
arg = None
def __init__(self):
type(self).arg = 'foo'
print(T.arg) # returns None
obj = T()
print(T.arg) # returns 'foo'
回答1:
You can use a class decorator:
def add_arg(cls):
if not hasattr(cls, "arg"):
cls.arg = 'foo'
return cls
@add_arg
class T(object):
pass
Or a custom metaclass:
class WithArg(type):
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
if not hasattr(cls, "arg"):
cls.arg = "foo"
return cls
# python 2
class T(object):
__metaclass__ = WithArg
# python 3
class T(metaclass=WithArg):
pass
But as others already mention this won't give you much more than plainly setting the class attribute in the class statement.
NB : if you want a computed attribute on the class itself, you'll have to either set it as a property on a custom metaclass
class WithProp(type):
@property
def arg(cls):
return "foo"
class T(object):
__metaclass__ = WithProp
T.arg
=> 'foo'
But arg
will only be available on the class object itself, not on it's instances:
T().arg
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'T' object has no attribute 'arg'
or write your own custom descriptor:
class ArgDescriptor(object):
def __get__(self, obj, cls=None):
return 42
class T(object):
arg = ArgDescriptor()
T.arg
=> 42
T().arg
=> 42
回答2:
You simply have to initialise the class variable when declaring it within the class
class T:
arg = 'foo' #this initialises the class instance
def __init__(self):
self.arg = 'bar' #this initialises the object instance
print(T.arg) # class instance returns 'foo'
obj = T()
print(T.arg) # class instance still returns 'foo'
print(obj.arg) # object instance returns 'bar'
回答3:
I create a static_init
decorator which calls a static_init
class method if it exists. This static_init
class method will run when the class decorator is evaluated which is when the class is defined - so not quite when the class is first referenced - but it is analogous to static initialization in other languages like Java.
Here is the decorator and example of how to use it to initialize a class variable on an enum class:
# pylint: disable=missing-docstring,no-member
import enum
def static_init(cls):
if getattr(cls, "static_init", None):
cls.static_init()
return cls
@static_init
class SomeEnum(enum.Enum):
VAL_A = enum.auto()
VAL_B = enum.auto()
VAL_C = enum.auto()
VAL_D = enum.auto()
@classmethod
def static_init(cls):
text_dict = {}
setattr(cls, 'text_dict', text_dict)
for value in cls:
text_dict[value.name.lower().replace("_", " ").title()] = value
def test_static_init():
assert SomeEnum.text_dict["Val A"] == SomeEnum.VAL_A
assert SomeEnum.text_dict["Val B"] == SomeEnum.VAL_B
assert SomeEnum.text_dict["Val C"] == SomeEnum.VAL_C
assert SomeEnum.text_dict["Val D"] == SomeEnum.VAL_D
来源:https://stackoverflow.com/questions/45505605/python-class-constructor-static