How to properly define class properties in Python - python

I've been looking for a way to define class properties in Python.
The expected behavior would be something intuitive like:
class A:
_access_count = 0
#classproperty
def value1(cls):
cls._access_count += 1
return 1
A.value1 # return 1
A().value1 # return 1
A._access_count # return 2
A.value1 = 2 # raise an AttributeError
I found related questions on SO, but none of them propose this exact feature.
This thread has a nice example of metaclass even though it doesn't really apply in that case. The accepted answer of this one propose a close solution but doesn't handle the setter mecanism.

Since it is ok to answer his own question I'll write what I've come up with so far.
class classproperty(property):
"""Class property works exactly like property."""
pass
def create_instance_property(cls_prop):
"""Create instance property from class property."""
fget, fset, fdel = None, None, None
if cls_prop.fget is not None :
fget = lambda self: cls_prop.fget(type(self))
if cls_prop.fset is not None :
fset = lambda self, value: cls_prop.fset(type(self), value)
if cls_prop.fdel is not None :
fdel = lambda self: cls_prop.fdel(type(self))
return property(fget, fset, fdel, cls_prop.__doc__)
def init_for_metaclass(cls, name, bases, dct):
"""__init__ method for a metaclass to handle class properties."""
super(type(cls), cls).__init__(name, bases, dct)
for key, prop in dct.items():
if isinstance(prop, classproperty):
setattr(cls, key, create_instance_property(prop))
setattr(type(cls), key, prop)
def handle_class_property(cls):
"""Class decorator to handle class properties."""
name = type(cls).__name__ + "_for_" + cls.__name__
metacls = type(name, (type(cls),), {"__init__": init_for_metaclass})
return metacls(cls.__name__, cls.__bases__, dict(cls.__dict__))
So far it works exactly as expected, even for inheritance cases:
#handle_class_property
class A(object):
_access_count = 0
#classproperty
def value1(cls):
print cls
cls._access_count += 1
return 1
class B(A):
_access_count = 0
#classproperty
def value2(cls):
cls._access_count += 1
return 2
#value2.setter
def value2(cls, value):
print(value)
a = A()
b = B()
assert (a.value1, A.value1) == (1,1)
assert (b.value1, B.value1) == (1,1)
assert (b.value2, B.value2) == (2,2)
assert B._access_count == 4
assert A._access_count == 2
B.value2 = 42 # This should print '42'
try: B.value1 = 42 # This should raise an exception
except AttributeError as e: print(repr(e))

Related

Is there an equivalent of partialmethod for properties?

I have a class with many parameters that are stored in a dictionary as below (in practice, it's a bit more complex, but this gives a good idea). I am able to 'autogenerate' get and set methods using partialmethod:
for a_name in ['x', 'y', 'z']:
locals()["get_" + a_name] = partialmethod(_get_arg,
arg_name=a_name)
locals()["set_" + a_name] = partialmethod(_set_arg,
arg_name=a_name)
Ideally, I wanted to use #property, but only if I can 'autogenerate' #property, #setter and #deleter. Would it be possible? The second snippet show the 'manually' added properties; I am looking into using an equivalent of partialmethod to avoid repetition and unmaintainable code.
class C:
def __init__(self):
self.kwargs = {'x' : 0, 'y' : 3, 'z' : True}
def __get_arg(self, arg_name):
assert arg_name in self.kwargs
return self.kwargs[arg_name]
def __set_arg(self, arg_name, value):
assert arg_name in self.kwargs
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
assert arg_name in self.kwargs
del self.kwargs[arg_name]
#property
def x(self):
return self.__get_arg('x')
#x.setter
def x(self, value):
self.__set_arg('x', value)
#x.deleter
def x(self):
self.__del_arg('x')
#property
def y(self):
return self.__get_arg('y')
#y.setter
def y(self, value):
self.__set_arg('y', value)
#y.deleter
def y(self):
self.__del_arg('y')
#property
def z(self):
return self.__get_arg('z')
#x.setter
def z(self, value):
self.__set_arg('z', value)
#x.deleter
def z(self):
self.__del_arg('z')
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
After struggling for awhile, I came up with the following partialproperty descriptor.
The __get__ method is called when an attribute is accessed. In it I call the
setter() method with the first argument of obj for the self paramater
followed by the unpacked self.args and self.kwargs.
class partialproperty:
"""Combine the functionality of property() and partialmethod()"""
def __init__(self, getter, setter=None, deleter=None, *args, **kwargs):
self.getter = getter
self.setter = setter
self.deleter = deleter
self.args = args
self.kwargs = kwargs
def __set_name__(self, owner, name):
self._name = name
self._owner = owner
def __get__(self, obj, objtype=None):
return self.getter(obj, *self.args, **self.kwargs)
def __set__(self, obj, value):
if self.setter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't set attribute: {self._name}")
self.setter(obj, *self.args, value, **self.kwargs)
def __delete__(self, obj):
if self.deleter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't delete attribute: {self._name}")
self.deleter(obj, *self.args, **self.kwargs)
You can use it in a class to create property attributes like so, much like you
would with partialmethod().
If you don't need a setter and deleter, you need to either use keyword
arguments for your args, or explicitly pass None.
class RGBA:
def __init__(self, rgba):
self.rgba = rgba
def _component_(self, idx):
return self.rgba[idx]
# using keyword args
red = partialproperty(_component_, idx=0)
green = partialproperty(_component_, idx=1)
blue = partialproperty(_component_, idx=2)
# this has the same effect but with positional args
alpha = partialproperty(_component_, None, None, 3)
Then in your instances the respective attributes will behave like properties.
>>> color = RGBA([0, 75, 255, 1])
>>> color.red
0
>>> color.green
75
>>> color.blue
255
>>> color.alpha
1
For your example it would look like:
class C:
def __init__(self, **kwargs):
self.kwargs = kwargs
def __get_arg(self, arg_name):
try:
return self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
def __set_arg(self, arg_name, value):
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
try:
del self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
x = partialproperty(__get_arg, __set_arg, __del_arg, "x")
y = partialproperty(__get_arg, __set_arg, __del_arg, "y")
z = partialproperty(__get_arg, __set_arg, __del_arg, "z")
>>> obj = C(x=24)
>>> obj.x
24
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
You can write your own descriptor type, and use the __set_name__ method that will get called on it (by the class creation machinery) to figure out what name it's been saved to in the class:
class MyProp:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner=None):
if instance is None:
return self
return instance._get_arg(self.name)
def __set__(self, instance, value):
instance._set_arg(self.name, value)
def __delete__(self, instance):
instance._del_arg(self.name)
You'd use it this way:
class C:
# define __init__, _get_arg, etc.
x = MyProp()
y = MyProp()
z = MyProp()
Note that because it's code from another class calling the _X_arg methods, you probably don't want to do name mangling, so I've changed the __ prefixes to just a single _.
For your specific example, an easier way to do this would probably to subclass
dict then define the magic methods __getattr__, __setattr__, and
__delattr__ which are each called when an attribute doesn't exist. Then you
can call super() to inherit the behavior from the dict class.
class AttrDict(dict):
attrs = ("x", "y", "z")
def __getattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__getitem__(name)
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__setitem__(name, value)
except KeyError:
raise AttributeError(name)
def __delattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__delitem__(name)
except KeyError:
raise AttributeError(name)
>>> obj = AttrDict(x=24)
>>> obj.x
24
>>> obj.a = 1
AttributeError: a
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
If you don't care about restricting to specific attributes it's even easier.
You can simply assign each __*attr__ method to its cooresponding
dict.__*item__ method, which is what gets called for subscription operations.
(ie obj[key])
class AttrDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
>>> obj = AttrDict({"a": 1})
>>> obj.a
1
>>> obj["b"] = 2
>>> obj.b
2
>>> del obj.b
>>> obj.b
KeyError

Clean way to implement setter and getter for lots of properties?

I have known the use of setter and getter for several properties, how could I trigger a same function when any property changes?
For example, the following codes add a setter to property a.
class AAA(object):
def __init__(self):
...
#property
def a(self):
...
#a.setter
def a(self, value):
...
If the class has a lot of properties like a, b, ... , z, and I want to print something like property xxx is modified when any property changes.
It is stupid to add the similar getter and setter one by one.
I have read some related questions and answers, but I do not find the solution for many properties.
How to trigger function on value change?
Using #property versus getters and setters
Metaprogramming, using __setattr__ to intercept modification:
class AAA(object):
def __setattr__(self, attr, value):
print("set %s to %s" % (attr, value))
super().__setattr__(attr, value)
aaa = AAA()
aaa.x = 17
# => set x to 17
print(aaa.x)
# => 17
You can do similarly with __getattr__ for reading access.
You can use descriptors. Descriptors are, in layman's terms, reusable properties. The advantage over the __getattr__ and __setattr__ hooks is that you have more fine-grained control over what attributes are managed by descriptors.
class MyDescriptor:
def __init__(self, default='default'):
self.default = default
def __set_name__(self, owner, name): # new in Python3.6
self.name = name
def __get__(self, instance, owner):
print('getting {} on {}'.format(self.name, instance))
# your getter logic here
# dummy implementation:
if instance is not None:
try:
return vars(instance)[self.name]
except KeyError:
return self.default
return self
def __set__(self, instance, value):
print('setting {} on {}'.format(self.name, instance))
# your getter logic here
# dummy implementation:
vars(instance)[self.name] = value
class MyClass:
a = MyDescriptor()
b = MyDescriptor()
_id = 1
# some logic for demo __repr__
def __init__(self):
self.c = 'non-descriptor-handled'
self.id = MyClass._id
MyClass._id += 1
def __repr__(self):
return 'MyClass #{}'.format(self.id)
Demo:
>>> m1 = MyClass()
>>> m2 = MyClass()
>>> m1.c
'non-descriptor-handled'
>>> m1.a
getting a on MyClass #1
'default'
>>> m1.b
getting b on MyClass #1
'default'
>>> m1.b = 15
setting b on MyClass #1
>>> m1.b
getting b on MyClass #1
15
>>> m2.b
getting b on MyClass #2
'default'
One year after asking this question, I find a more elgant way to add getter and setter to multiple similar properties.
Just make a more 'abstract' function which returns decorated property. And pass each of these properties to this function with a for loop. Then the getter and setter of all these properties are added.
def propABC(arg):
# arg: 'a', 'b', 'c'
#property
def prop(self):
_arg = '_' + arg
return getattr(self, _arg)
#prop.setter
def prop(self, val):
_arg = '_' + arg
setattr(self, _arg, val)
print(f"Set prop {_arg}")
return prop
for key in ['a', 'b', 'c']:
exec(f"{key} = propABC('{key}')")

Python design - initializing, setting, and getting class attributes

I have a class in which a method first needs to verify that an attribute is present and otherwise call a function to compute it. Then, ensuring that the attribute is not None, it performs some operations with it. I can see two slightly different design choices:
class myclass():
def __init__(self):
self.attr = None
def compute_attribute(self):
self.attr = 1
def print_attribute(self):
if self.attr is None:
self.compute_attribute()
print self.attr
And
class myclass2():
def __init__(self):
pass
def compute_attribute(self):
self.attr = 1
return self.attr
def print_attribute(self):
try:
attr = self.attr
except AttributeError:
attr = self.compute_attribute()
if attr is not None:
print attr
In the first design, I need to make sure that all the class attributes are set to None in advance, which can become verbose but also clarify the structure of the object.
The second choice seems to be the more widely used one. However, for my purposes (scientific computing related to information theory) using try except blocks everywhere can be a bit of an overkill given that this class doesn't really interact with other classes, it just takes data and computes a bunch of things.
Firstly, you can use hasattr to check if an object has an attribute, it returns True if the attribute exists.
hasattr(object, attribute) # will return True if the object has the attribute
Secondly, You can customise attribute access in Python, you can read more about it here: https://docs.python.org/2/reference/datamodel.html#customizing-attribute-access
Basically, you override the __getattr__ method to achieve this, so something like:
class myclass2():
def init(self):
pass
def compute_attr(self):
self.attr = 1
return self.attr
def print_attribute(self):
print self.attr
def __getattr__(self, name):
if hasattr(self, name) and getattr(self, name)!=None:
return getattr(self, name):
else:
compute_method="compute_"+name;
if hasattr(self, compute_method):
return getattr(self, compute_method)()
Make sure you only use getattr to access the attribute within __getattr__ or you'll end up with infinite recursion
Based on the answer jonrsharpe linked, I offer a third design choice. The idea here is that no special conditional logic is required at all either by the clients of MyClass or by code within MyClass itself. Instead, a decorator is applied to a function that does the (hypothetically expensive) computation of the property, and then that result is stored.
This means that the expensive computation is done lazily (only if a client tries to access the property) and only performed once.
def lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
#property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazyprop
class MyClass(object):
#lazyprop
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
Output
{}
Generating attr
1
{'_lazy_attr': 1}
1
Edit
Application of Cyclone's answer to OP's context:
class lazy_property(object):
'''
meant to be used for lazy evaluation of an object attribute.
property should represent non-mutable data, as it replaces itself.
'''
def __init__(self, fget):
self.fget = fget
self.func_name = fget.__name__
def __get__(self, obj, cls):
if obj is None:
return None
value = self.fget(obj)
setattr(obj, self.func_name, value)
return value
class MyClass(object):
#lazy_property
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
The output is identical to above.

Implement _del_ method for a class with __getattribute__ overriden

Taking this question as a pointer, let's say there exists a class like the following:
class Container(object):
def __init__(self, **kwargs):
self._meta = defaultdict(lambda: None)
for attr, value in kwargs.iteritems():
self._meta[attr] = value
def __getattr__(self, key):
try:
return self._meta[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
if key in ('_meta', '_hasattr'):
super(Container, self).__setattr__(key, value)
else:
self._meta[key] = value
This allows the following behavior:
c = Container()
c.a = 1
print(c.a) # 1
print(c.b) # None
Question: What is the best way to implement an operator such that the following works:
# Should delete the value of a from Container._meta
del c.a
Of course, one could obviously implement a method like,
def _delete(self, key):
...
But is there way to re-use a python operator to do this?
Just define the __delattr__ method:
def __delattr__(self, key):
del self._meta[key]

How to make a class property? [duplicate]

This question already has answers here:
Using property() on classmethods
(19 answers)
Closed 3 years ago.
In python I can add a method to a class with the #classmethod decorator. Is there a similar decorator to add a property to a class? I can better show what I'm talking about.
class Example(object):
the_I = 10
def __init__( self ):
self.an_i = 20
#property
def i( self ):
return self.an_i
def inc_i( self ):
self.an_i += 1
# is this even possible?
#classproperty
def I( cls ):
return cls.the_I
#classmethod
def inc_I( cls ):
cls.the_I += 1
e = Example()
assert e.i == 20
e.inc_i()
assert e.i == 21
assert Example.I == 10
Example.inc_I()
assert Example.I == 11
Is the syntax I've used above possible or would it require something more?
The reason I want class properties is so I can lazy load class attributes, which seems reasonable enough.
Here's how I would do this:
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class Bar(object):
_bar = 1
#classproperty
def bar(cls):
return cls._bar
#bar.setter
def bar(cls, value):
cls._bar = value
# test instance instantiation
foo = Bar()
assert foo.bar == 1
baz = Bar()
assert baz.bar == 1
# test static variable
baz.bar = 5
assert foo.bar == 5
# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
The setter didn't work at the time we call Bar.bar, because we are calling
TypeOfBar.bar.__set__, which is not Bar.bar.__set__.
Adding a metaclass definition solves this:
class ClassPropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClassPropertyDescriptor:
return obj.__set__(self, value)
return super(ClassPropertyMetaClass, self).__setattr__(key, value)
# and update class define:
# class Bar(object):
# __metaclass__ = ClassPropertyMetaClass
# _bar = 1
# and update ClassPropertyDescriptor.__set__
# def __set__(self, obj, value):
# if not self.fset:
# raise AttributeError("can't set attribute")
# if inspect.isclass(obj):
# type_ = obj
# obj = None
# else:
# type_ = type(obj)
# return self.fset.__get__(obj, type_)(value)
Now all will be fine.
If you define classproperty as follows, then your example works exactly as you requested.
class classproperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
The caveat is that you can't use this for writable properties. While e.I = 20 will raise an AttributeError, Example.I = 20 will overwrite the property object itself.
[answer written based on python 3.4; the metaclass syntax differs in 2 but I think the technique will still work]
You can do this with a metaclass...mostly. Dappawit's almost works, but I think it has a flaw:
class MetaFoo(type):
#property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
This gets you a classproperty on Foo, but there's a problem...
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
What the hell is going on here? Why can't I reach the class property from an instance?
I was beating my head on this for quite a while before finding what I believe is the answer. Python #properties are a subset of descriptors, and, from the descriptor documentation (emphasis mine):
The default behavior for attribute access is to get, set, or delete the
attribute from an object’s dictionary. For instance, a.x has a lookup chain
starting with a.__dict__['x'], then type(a).__dict__['x'], and continuing
through the base classes of type(a) excluding metaclasses.
So the method resolution order doesn't include our class properties (or anything else defined in the metaclass). It is possible to make a subclass of the built-in property decorator that behaves differently, but (citation needed) I've gotten the impression googling that the developers had a good reason (which I do not understand) for doing it that way.
That doesn't mean we're out of luck; we can access the properties on the class itself just fine...and we can get the class from type(self) within the instance, which we can use to make #property dispatchers:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
#property
def thingy(self):
return type(self).thingy
Now Foo().thingy works as intended for both the class and the instances! It will also continue to do the right thing if a derived class replaces its underlying _thingy (which is the use case that got me on this hunt originally).
This isn't 100% satisfying to me -- having to do setup in both the metaclass and object class feels like it violates the DRY principle. But the latter is just a one-line dispatcher; I'm mostly okay with it existing, and you could probably compact it down to a lambda or something if you really wanted.
If you use Django, it has a built in #classproperty decorator.
from django.utils.decorators import classproperty
For Django 4, use:
from django.utils.functional import classproperty
I think you may be able to do this with the metaclass. Since the metaclass can be like a class for the class (if that makes sense). I know you can assign a __call__() method to the metaclass to override calling the class, MyClass(). I wonder if using the property decorator on the metaclass operates similarly.
Wow, it works:
class MetaClass(type):
def getfoo(self):
return self._foo
foo = property(getfoo)
#property
def bar(self):
return self._bar
class MyClass(object):
__metaclass__ = MetaClass
_foo = 'abc'
_bar = 'def'
print MyClass.foo
print MyClass.bar
Note: This is in Python 2.7. Python 3+ uses a different technique to declare a metaclass. Use: class MyClass(metaclass=MetaClass):, remove __metaclass__, and the rest is the same.
As far as I can tell, there is no way to write a setter for a class property without creating a new metaclass.
I have found that the following method works. Define a metaclass with all of the class properties and setters you want. IE, I wanted a class with a title property with a setter. Here's what I wrote:
class TitleMeta(type):
#property
def title(self):
return getattr(self, '_title', 'Default Title')
#title.setter
def title(self, title):
self._title = title
# Do whatever else you want when the title is set...
Now make the actual class you want as normal, except have it use the metaclass you created above.
# Python 2 style:
class ClassWithTitle(object):
__metaclass__ = TitleMeta
# The rest of your class definition...
# Python 3 style:
class ClassWithTitle(object, metaclass = TitleMeta):
# Your class definition...
It's a bit weird to define this metaclass as we did above if we'll only ever use it on the single class. In that case, if you're using the Python 2 style, you can actually define the metaclass inside the class body. That way it's not defined in the module scope.
def _create_type(meta, name, attrs):
type_name = f'{name}Type'
type_attrs = {}
for k, v in attrs.items():
if type(v) is _ClassPropertyDescriptor:
type_attrs[k] = v
return type(type_name, (meta,), type_attrs)
class ClassPropertyType(type):
def __new__(meta, name, bases, attrs):
Type = _create_type(meta, name, attrs)
cls = super().__new__(meta, name, bases, attrs)
cls.__class__ = Type
return cls
class _ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, owner):
if self in obj.__dict__.values():
return self.fget(obj)
return self.fget(owner)
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
return self.fset(obj, value)
def setter(self, func):
self.fset = func
return self
def classproperty(func):
return _ClassPropertyDescriptor(func)
class Bar(metaclass=ClassPropertyType):
__bar = 1
#classproperty
def bar(cls):
return cls.__bar
#bar.setter
def bar(cls, value):
cls.__bar = value
bar = Bar()
assert Bar.bar==1
Bar.bar=2
assert bar.bar==2
nbar = Bar()
assert nbar.bar==2
I happened to come up with a solution very similar to #Andrew, only DRY
class MetaFoo(type):
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.thingy})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
#property
def thingy(cls):
if not inspect.isclass(cls):
cls = type(cls)
return cls._thingy
#thingy.setter
def thingy(cls, value):
if not inspect.isclass(cls):
cls = type(cls)
cls._thingy = value
class Foo(metaclass=MetaFoo):
_thingy = 23
class Bar(Foo)
_thingy = 12
This has the best of all answers:
The "metaproperty" is added to the class, so that it will still be a property of the instance
Don't need to redefine thingy in any of the classes
The property works as a "class property" in for both instance and class
You have the flexibility to customize how _thingy is inherited
In my case, I actually customized _thingy to be different for every child, without defining it in each class (and without a default value) by:
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.services, '_thingy': None})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
If you only need lazy loading, then you could just have a class initialisation method.
EXAMPLE_SET = False
class Example(object):
#classmethod
def initclass(cls):
global EXAMPLE_SET
if EXAMPLE_SET: return
cls.the_I = 'ok'
EXAMPLE_SET = True
def __init__( self ):
Example.initclass()
self.an_i = 20
try:
print Example.the_I
except AttributeError:
print 'ok class not "loaded"'
foo = Example()
print foo.the_I
print Example.the_I
But the metaclass approach seems cleaner, and with more predictable behavior.
Perhaps what you're looking for is the Singleton design pattern. There's a nice SO QA about implementing shared state in Python.

Categories