Package: class-property
Installpipinstallclass_propertyUtilitiesclass_value - Hold a single value for a class and all instances.class_property - Like a property, but works onMyClass.class_prop = valuemetaclass - Create a metaclass that looks for class_value and class_property to register them.decorate - Decorate a class to register any class_value and class_property.A class must use the metaclass or decorate functions.Subclasses do not need to use decorate or metaclass.
The metaclass is how the class object works with the descriptor.RunClass value usage.Example:fromclass_propertyimportclass_value# doesn't matter if class_value.decorate, class_property.decorate, or decorate (same with metaclass)classMyClass(object,metaclass=class_value.metaclass()):value=class_value(1)mc=MyClass()assertmc.value==1assertMyClass.value==1MyClass.value=3assertmc.value==3assertMyClass.value==3mc.value=2assertmc.value==2assertMyClass.value==2classSubClass(MyClass):hello=class_value("World")sub=SubClass()SubClass.hello='name'assertsub.hello=='name'assertSubClass.hello=='name'sub.hello='John Doe'assertsub.hello=='John Doe'assertSubClass.hello=='John Doe'sub.value=7assertSubClass.value==7assertsub.value==7assertmc.value==7assertMyClass.value==7Class property usage.Example:fromclass_propertyimportclass_value,class_property,decorate,metaclassglobalGLOBGLOB='Hello'defget_glob():"""Return the global GLOB value"""globalGLOBreturnGLOBdefset_glob(value):globalGLOBGLOB=value# doesn't matter if class_value.decorate, class_property.decorate, or decorate (same with metaclass)@decorateclassMyClass(object):_VALUE=None@class_propertydefvalue(self):returnMyClass._VALUE@value.setterdefvalue(self,value):MyClass._VALUE=value# Also works with no arguments@class_propertydefvalue2():returnMyClass._VALUE@value2.setterdefvalue2(value):MyClass._VALUE=valueglob=class_property(get_glob,set_glob)mc=MyClass()assertmc.valueisNoneassertMyClass.valueisNoneMyClass.value=3assertmc.value==3assertMyClass.value==3mc.value=2assertmc.value==2assertMyClass.value==2assertmc.value2==2assertMyClass.value2==2mc.value2=5assertmc.value==5assertMyClass.value==5assertmc.value2==5assertMyClass.value2==5assertMyClass.glob=='Hello'assertmc.glob=='Hello'MyClass.glob='Jack'assertMyClass.glob=='Jack'assertmc.glob=='Jack'mc.glob='Jill'assertMyClass.glob=='Jill'assertmc.glob=='Jill'classSubClass(MyClass):passsub=SubClass()sub.glob='John'assertSubClass.glob=='John'assertsub.glob=='John'assertMyClass.glob=='John'assertmc.glob=='John'InheritanceAfter usingdecorateormetaclassto create the class using a metaclassclass_valueandclass_propertywork as expected.
When you inherit from aclass_propertyclass newclass_valueandclass_propertyattributes work as expected as well.
If you try to override aclass_valueorclass_propertythings get complicated.If you just want to change fromclass_valueto aclass_propertyset a new variable like normal.
This will override the parent class attribute as well.Example:fromclass_propertyimportclass_value,class_propertyclassMyClass(object,metaclass=class_value.metaclass()):value=class_value(1)classSubClass(MyClass):_VALUE=2# This replaces "MyClass.value". class_value/class_property uses the metaclass, so we have to replace.# We can only change to a new object if we change the metaclass.@class_propertydefvalue(self):returnSubClass._VALUE@value.setterdefvalue(self,val):SubClass._VALUE=valclassSetValueClass(MyClass):value=3# Check that SetValueClass just changes the current valueassertMyClass.value==3assertSubClass.value==3assertSetValueClass.value==3mc=MyClass()mc.value=4sub=SubClass()sub.value=7assertsub.value==7assertSubClass.value==7assertSubClass._VALUE==7# MyClass.value is the new class_property from SubClass, because of the shared metaclassassertmc.value==7assertMyClass.value==7assertSetValueClass.value==7If you want a disconnected newclass_propertythen you need a new metaclass.Example:fromclass_propertyimportclass_propertyclassMyClass(object,metaclass=class_property.metaclass()):_VALUE1=1@class_propertydefvalue(self):returnMyClass._VALUE1@value.setterdefvalue(self,val):MyClass._VALUE1=valclassSubClass(MyClass,metaclass=class_property.metaclass(MyClass)):_VALUE2=2# Because we have a different metaclass `SubClass.value` is different from `MyClass.value`@class_propertydefvalue():returnSubClass._VALUE2@value.setterdefvalue(val):SubClass._VALUE2=valmc=MyClass()mc.value=4# SubClass.value is using a different metaclass and is disconnected from MyClasssub=SubClass()sub.value=7assertsub.value==7assertSubClass.value==7assertSubClass._VALUE2==7# MyClass.value is using a different metaclass and is disconnected from SubClassassertmc.value==4assertMyClass.value==4assertMyClass._VALUE1==4