Can decorator wrap setter and getter in python? - python

I want to build various setter and getter. Fot not copy and paste the code, I thought something to solve it. Can decorator do it?
#property
def !!variable_name!!(self):
return self.__!!variable_name!!
#!!variable_name!!.setter
def !!variable_name!!(self, input):
self.__!!variable_name!! = input
Is it possible like macro in C?

It's unclear why you would want to do something like this—create a property with setter that ignores its value argument—but the answer is "Yes", you can do it by creating a function that returns a custom property object:
However you can't use # syntax to apply it. Instead you have to utilize it as shown:
def attribute_property(name, input_value):
STORAGE_NAME = '_' + name
#property
def prop(self):
return getattr(self, STORAGE_NAME)
#prop.setter
def prop(self, ignored):
setattr(self, STORAGE_NAME, input_value)
return prop
# EXAMPLE USAGE
class Person(object):
name = attribute_property('name', 'Monty')
def __init__(self, name, age):
self.name = name # ignores value of passed "name" argument!
self.age = age
user = Person('Rodrigo', 42)
print('user.name: {!r}'.format(user.name))
print('user.age: {!r}'.format(user.age))
Output:
user.name: 'Monty'
user.age: 42

Simple answer: Yes, that's possible using the descriptor protocol. For example you want to save variables with a leading underscore and access them without the leading underscore such a descriptor would work:
from six import string_types
class DescriptorSingleLeadingUnderscore(object):
def __init__(self, attr, doc=""):
if not isinstance(attr, string_types):
# Not a string so take the documentation (if avaiable) and name
# from the method.
if attr.__doc__:
doc = attr.__doc__
attr = attr.__name__
self.__doc__ = doc # Set the documentation of the instance.
self.attr = '_' + attr # Add leading underscore to the attribute name
def __get__(self, instance, owner=None):
if instance is None:
return self
return getattr(instance, self.attr, None)
def __set__(self, instance, value):
setattr(instance, self.attr, value)
def __delete__(self, instance):
delattr(instance, self.attr)
class X(object):
someproperty = DescriptorSingleLeadingUnderscore('someproperty')
someproperty1 = DescriptorSingleLeadingUnderscore('someproperty1')
someproperty2 = DescriptorSingleLeadingUnderscore('someproperty2')
someproperty3 = DescriptorSingleLeadingUnderscore('someproperty3')
#DescriptorSingleLeadingUnderscore
def it_also_works_as_decorator(self):
pass # this code is never executed!
And a test case:
>>> x = X()
>>> x.someproperty = 100
>>> x.someproperty
100
>>> x._someproperty
100
>>> x.it_also_works_as_decorator = 100
>>> x.it_also_works_as_decorator
100
>>> x._it_also_works_as_decorator
100

Related

How best to implement a property where the setter can only be called once?

I trying to implement a class property where the setter can only be called once and am wondering how best to achieve this ? And how to make it most 'pythonic'?
Options I have considered:
Subclass and extend the builtin property.
Decorate the setter of a property.
Add an attribute which persists how often each setter has been set.
Any other ideas?
And suggestions how to best implement?
If you are using it a lot, along with other property functionality, subclassing property is appropriate.
It is a bit tricky, due to the way property works - when one calls
#prop.setter, a new instance of the property is created. The subclass bellow will work.
class FuseProperty(property):
def setter(self, func):
def fuse(instance, value):
name = f"_fuse_{self.fget.__name__}"
if not getattr(instance, name, False):
func(instance, value)
setattr(instance, name, True)
return super().setter(lambda instance, value: fuse(instance, value))
Here is it in use.
In [24]: class A:
...: #FuseProperty
...: def a(self):
...: return self._a
...: #a.setter
...: def a(self, value):
...: self._a = value
...:
In [25]: a = A()
In [26]: a.a = 23
In [27]: a.a
Out[27]: 23
In [28]: a.a = 5
In [29]: a.a
Out[29]: 23
However, if this "fuse" property is all you need, and no other code is being added to the getters and setters, it can be much simpler: you can create a brand new "Descriptor" class, using the same mechanisms used by property - this can be much better, as your "fuse" properties can be built in a single line, with no need for a setter and getter methods.
All that is needed is a class with __get__ and __set__ methods - we can add __set_name__ to get the new property name automatically (which property itself does not, so we get the name from the fget method above)
class FuseAttribute:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, f"_{self.name}")
def __set__(self, instance, value):
if not getattr(instance, f"_fuse_{self.name}", False):
setattr(instance, f"_{self.name}", value)
# add an else clause for optionally raising an error
setattr(instance, f"_fuse_{self.name}", True)
And using it:
In [36]: class A:
...: a = FuseAttribute()
...:
In [37]: a = A()
In [38]: a.a = 23
In [39]: a.a
Out[39]: 23
In [40]: a.a = 5
In [41]: a.a
Out[41]: 23
Properties in Python are just descriptors, and it's relatively easy to implement your own that does exactly what you want:
class SetOnceProperty:
def __init__(self, name):
self.storage_name = '_' + name
def __get__(self, obj, owner=None):
return getattr(obj, self.storage_name)
def __set__(self, obj, value):
if hasattr(obj, self.storage_name):
raise RuntimeError(f'{self.storage_name[1:]!r} property already set.')
setattr(obj, self.storage_name, value)
def __delete___(self, obj):
delattr(obj, self.storage_name)
class Test:
test_attr = SetOnceProperty('test_attr')
def __init__(self, value):
self.test_attr = value*2 # Sets property.
test = Test(21)
print(test.test_attr) # -> 42
test.test_attr = 13 # -> RuntimeError: 'test_attr' property already set.
I have often preferred this way; "explicit is better than implicit":
class MyError(Exception):
...
NOT_SET = object()
class C:
def set_my_property(self, spam, eggs, cheese):
"""This sets the property.
If it's already set, you'll get an error. Donna do dat.
"""
if getattr(self, "_my_property", NOT_SET) is NOT_SET:
self._my_property = spam, eggs, cheese
return
raise MyError("I said, Donna do dat.")
#property
def my_property(self):
return self._my_property
Testing:
c=C()
c.set_my_property("spam", "eggs", "cheese")
assert c.my_property == ("spam", "eggs", "cheese")
try:
c.set_my_property("bacon", "butter", "coffee")
except MyError:
pass
This is simple enough and more general. A decorator for a function to be called only once, and ignore subsecuent calls.
def onlyonce(func):
#functools.wraps(func)
def decorated(*args):
if not decorated.called:
decorated.called = True
return self.func(*args)
decorated.called = False
return decorated
use like this
class A:
#property
def x(self):
...
#x.setter
#onlyonce
def x(self, val):
...
Or you can define a descriptor:
class Desc:
def __get__(self, inst, own):
return self._value
def __set__(self, inst, value):
if not hasattr(self, _value):
self._value = value
And use like this:
class A:
x = Desc()
It sounds like the attrs project would be helpful for your use case. You can realize "frozen" attributes in the following way:
import attr
#attr.s
class Test:
constant = attr.ib(on_setattr=attr.setters.frozen)
test = Test('foo')
test.constant = 'bar' # raises `attr.exceptions.FrozenAttributeError`
Note that it also supports validators via #constant.validator (see the example at the end of attr.ib docs).

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}')")

Defining setter in a shorter way

Currently when I want to define a setter and leave getter alone I do this:
#property
def my_property(self):
return self._my_property
#my_property.setter
def my_property(self, value):
value.do_some_magic()
self._my_property = value
Is there any way to make it shorter? I'd like to skip this part as it always look the same:
#property
def my_property(self):
return self._my_property
There's no out of the box solution, but you can try something like this:
def defprop(name):
def getter(self):
return getattr(self, name)
return property(getter)
class C(object):
# ...
my_dictionary = defprop('_my_dictionary')
# ...
That does not save you that many keystrokes though, you still have to duplicate the attribute name. Besides it's less explicit.
Update: after thinking a bit, I've come up with this descriptor-based hackish trick (disclaimer: this is done just for a demonstration, I don't imply it's a good practice unless you have a damn good reason to do so):
class with_default_getter(object):
def __init__(self, func):
self._attr_name = '_{0.__name__}'.format(func)
self._setter = func
def __get__(self, obj, type):
return getattr(obj, self._attr_name)
def __set__(self, obj, value):
return self._setter(obj, value)
Usage:
class C(object):
#with_default_getter
def my_property(self, value):
print 'setting %s'
self._my_property = value
>>> c = C()
>>> c.my_property = 123
setting 123
>>> c.my_property
123
This is pretty much the same as #georg suggests, just unfolds the implementation down to descriptors.
You can make a decorator that auto-creates the getter, following the underscores convention:
def setter(fn):
def _get(self):
return getattr(self, '_' + fn.__name__)
def _set(self, val):
return fn(self, val)
return property(_get, _set)
or more concisely, if you like this style more:
def setter(fn):
return property(
lambda self: getattr(self, '_' + fn.__name__),
fn)
Usage:
class X(object):
#setter
def my_property(self, value):
self._my_property = value + 1
x = X()
x.my_property = 42
print x.my_property # 43
There is no shortcut that I am aware of- remember explicit is better than implicit (from the Zen of python).
It could be that in your code so far, a property is always like that - but you could at some point write a a property getter which fetches an entirely calculated value - in which case your property getter and setter wont look like that at all.
Haveing said that you could write a wrapper which provides those simple default methods as part of the wrapper, if you wish.
def set_my_property(self, value):
value.do_some_magic()
self._my_property = value
my_property = property(fset=set_my_property)

Catching changes to a mutable attribute in python

I am using properties to execute some code every time there is a change to an attribute, like this:
class SomeClass(object):
def __init__(self,attr):
self._attr = attr
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
And this works great:
>>> a = SomeClass(5)
>>> a.attr = 10
Do some code here every time attr changes
But if I store a mutable object in attr instead, attr can be modified directly, bypassing the setter and my change-detection code:
class Container(object):
def __init__(self,data):
self.data = data
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
>>>
Let's assume that attr is only ever going to be used to store an object of type Container. Is there an elegant way to modify SomeClass and/or Container to make SomeClass execute _on_change whenever the Container object stored in attr is modified? In other words, I want my output to be:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
Here is another solution. Some kind of proxy class. You dont need to modify any classes to monitor attributes changes in them, only wrap object in ChangeTrigger derived class with ovverriden _on_change function:
class ChangeTrigger(object):
def __getattr__(self, name):
obj = getattr(self.instance, name)
# KEY idea for catching contained class attributes changes:
# recursively create ChangeTrigger derived class and wrap
# object in it if getting attribute is class instance/object
if hasattr(obj, '__dict__'):
return self.__class__(obj)
else:
return obj
def __setattr__(self, name, value):
if getattr(self.instance, name) != value:
self._on_change(name, value)
setattr(self.instance, name, value)
def __init__(self, obj):
object.__setattr__(self, 'instance', obj)
def _on_change(self, name, value):
raise NotImplementedError('Subclasses must implement this method')
Example:
class MyTrigger(ChangeTrigger):
def _on_change(self, name, value):
print "New value for attr %s: %s" % (name, value)
class Container(object):
def __init__(self, data):
self.data = data
class SomeClass(object):
attr_class = 100
def __init__(self, attr):
self.attr = attr
self.attr_instance = 5
>>> a = SomeClass(5)
>>> a = MyTrigger(a)
>>>
>>> a.attr = 10
New value for attr attr: 10
>>>
>>> b = SomeClass(Container(5))
>>> b = MyTrigger(b)
>>>
>>> b.attr.data = 10
New value for attr data: 10
>>> b.attr_class = 100 # old value = new value
>>> b.attr_instance = 100
New value for attr attr_instance: 100
>>> b.attr.data = 10 # old value = new value
>>> b.attr.data = 100
New value for attr data: 100
Here is a version of SomeClass and Container that I think has the behavior you are looking for. The idea here being that modifications to Container will call the _on_change() function of the SomeClass instance that is associated with it:
class Container(object):
def __init__(self, data):
self.data = data
def __setattr__(self, name, value):
if not hasattr(self, name) or getattr(self, name) != value:
self.on_change()
super(Container, self).__setattr__(name, value)
def on_change(self):
pass
class SomeClass(object):
def __init__(self, attr):
self._attr = attr
self._attr.on_change = self._on_change
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
Example:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
>>> b.attr.data = 10 # on_change() not called if the value isn't changing
>>> b.attr.data2 = 'foo' # new properties being add result in an on_change() call
Do some code here every time attr changes
Note that the only change to SomeClass was the second line in __init__(), I just included the full code for completeness and easy testing.
If you want to track changes and don't want to mess with juggling with on_change() methods in different classes you could use functools.partial in the way shown starting here.
This way you can wrap your data and hide it totally. Get/change will be possible only via some methods melded inside that object.
NB: python has no private properties and on convention that we all are grownups and act against rules. In your case users of your api shouldn't change data on container (after creation) directly.
NB: here for those who may be interested in other ways...

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