pythonic way to declare multiple class instance properties as numbers? - python

I have a class with several properties, each of which has to be a number. After repeating the same code over and over again I think there is a more pythonic way to declare multiple class instance properties as numbers.
Right now I set each property value to None and raise a type error if the value is set to a non number type. I'd prefer to set the property type to a number when the property is initialized.
Thanks!
Example:
import numbers
class classWithNumbers(object):
def __init__(self):
self._numProp1 = None
self._numProp2 = None
#property
def numProp1(self):
return self._numProp1
#numProp1.setter
def numProp1(self,value):
if not isinstance(value, numbers.Number): #repeated test for number
raise TypeError("Must be number")
self._numProp1 = value
#property
def numProp2(self):
return self._numProp2
#numProp2.setter
def numProp(self,value):
if not isinstance(value, numbers.Number):
raise TypeError("Must be number")
self._numProp2 = value
Also, I actually have this wrapped into a method that is repeated at each property setter:
def isNumber(value):
if not isinstance(value, numbers.Number):
raise TypeError("Must be number")

If every property of this class should be a number you can implement custom __setattr__ method:
class ClassWithNumbers(object):
def __init__(self):
self.num_prop1 = 0
self.num_prop2 = 0
def __setattr__(self, name, value):
if not isinstance(value, numbers.Number):
raise TypeError("Must be number")
super(ClassWithNumbers, self).__setattr__(name, value)
From documentation: __setattr__ (is) called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.
More general approach would be to not allow type of once assigned attribute to change:
class ClassWithUnchangeableTypes(object):
def __init__(self):
self.num_prop1 = 0
self.num_prop2 = 0
def __setattr__(self, name, value):
if hasattr(self, name): # this means that we assigned value in the past
previous_value_type = type(getattr(self, name))
if not isinstance(value, previous_value_type):
raise TypeError("Must be {}".format(previous_value_type))
super(ClassWithUnchangeableTypes, self).__setattr__(name, value)
Speaking of pythonic, from pep8:
Class names should normally use the CapWords convention.
Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.

A fairly modern (python 3.5+) and pythonic way is using type hints
#property
def numProp1(self):
return self._numProp1
#numProp1.setter
def numProp1(self,value: int):
self._numProp1 = value
A more compatible way is to try to convert to int, which will then throw an exception for you if that fails. It might also have unwanted behaviour like accepting floats:
#property
def numProp1(self):
return self._numProp1
#numProp1.setter
def numProp1(self,value):
self._numProp1 = int(value)
But there's already nothing wrong with your implementation in general.
If you do not want to explicitly declare getters and setters, you could check their type when used, not when assigned.

The most Pythonic way is probably to call the int constructor and let it throw an exception:
class ClassWithNumbers(object):
def __init__(self, v1, v2):
self.numprop1 = int(v1)
self.numprop2 = int(v2)
if the numprops are part of your interface then creating #property accessors would be appropriate. You can also implement your own descriptor:
class Number(object):
def __init__(self, val=0):
self.__set__(self, val)
def __get__(self, obj, cls=None):
return self.val
def __set__(self, obj, val):
try:
self.val = int(val)
except ValueError as e:
raise TypeError(str(e))
class ClassWithNumbers(object):
numprop1 = Number(42)
numprop2 = Number(-1)
usage:
c = ClassWithNumbers()
print c.numprop1
c.numprop1 += 1
print c.numprop1
c.numprop1 = 'hello'

Related

Do I need a class factory function when using a descriptor that require initialization?

Consider this toy example where I use a descriptor to validate that a particular value doesn't exceed certain maximum value
class MaxValidator:
def __init__(self, max=10):
self.max = max
def __set__(self, obj, value):
if value > self.max:
raise RuntimeError(f"value {value} must be smaller than {self.max}")
obj._value = value
def __get__(self, obj):
return obj._value
class MyValue:
value = MaxValidator(max=5)
def __init__(self, value):
self.value = value # implicit validation takes place here
What happens now if I want a validator with a maximum value different than 5?
The only solution I got was to create a class factory function:
def MyValueFactory(maximum):
class _MyValue:
value = MaxValidator(max=maximum)
def __init__(self, value):
self.value = value # implicit validation takes place here
return _MyValue
MyValue = MyValueFactory(5) # this class has the same validator as the previous MyValue
I think a class factory function is a bit of overkill. Is there another pattern I can use when dealing with "parameterized" python descriptors?
Attempt to insert the descriptor in __init__
class MyValue:
def __init__(self, value, maximum=5):
self.value = MaxValidator(max=maximum)
# but the following is not possible anymore
self.value = value #this is reassignment to self.value, the descriptor is lost
Don´t forget that at execution time, the descriptors method for __get__ and __set__ have access to the instance and class where they live in.
So, all you need is a class attribute (and even an instance attribute) to configure the behavior of your descriptor class-wide.
That can be done either with a fixed name, that will affect all descriptors of a certain kind, or better yet, a descriptor could check for its name - which can also be automatically attributed, and use that as a prefix for the maximum.
class MaxValidator:
def __init__(self, default_max=10):
self.default_max = default_max
def __set_name__(self, owner, name):
self.name = name
def __set__(self, obj, value):
# the line bellow retrieves <descriptor_name_maximum> attribute on the instance
maximum = getattr(obj, self.name + "_maximum", self.default_max)
if value > maximum:
raise RuntimeError(f"value {value} must be smaller than {maximum}")
obj._value = value
def __get__(self, obj, owner):
return obj._value
class MyValue:
value = MaxValidator()
def __init__(self, value, custom_max=5):
self.value_maximum=custom_max
self.value = value # implicit validation takes place here
This is one other way of doing it.
The factory function is not that terrible as well - but you seem to have forgotten the descriptor can check the class and instances themselves.
As for creating the descriptor itself inside __init__ - it is possible, but one have to keep in mind the descriptor must be a class attribute, and whenver you create a new instance of the class, the descriptor would be overriden with the new configurations:
class MyValue:
# don't do this: each new instance will reset the configs
# of previously created instances:
def __init__(self, value, maximum=5):
# The descriptor must be set in the class itself:
self.__class__.value = MaxValidator(max=maximum)
# the following will activate the descriptor defined above:
self.value = value

Intuative way to inherit validating classes

I've been using this style of inheritance to validate values set on instances of objects, but I'm wondering if there is a more fluent way to do this.
I'm following a spec where items of a certain classification (Foo) contain elements of a certain composition (Fe).
class Typed:
def __set__(self, obj, value):
assert isinstance(value, self._type), 'Incorrect type'
class Integer(Typed):
_type = int
class Float(Typed):
_type = float
class Positive(Typed):
def __set__(self, obj, value):
super().__set__(obj, value)
assert value >= 0, 'Positive Values Only Accepted'
class PositiveInteger(Integer, Positive):
pass
class PositiveFloat(Float, Positive):
pass
class Sized(Typed):
def __set__(self, obj, value):
super().__set__(obj, value)
assert value <= 2**self.size-1, f'{value} is too High'
class Fe(Sized, PositiveInteger):
name = 'Integer, 8 bit unsigned'
size = 8
class Foo(Fe):
name = 'Classificaion1'
def __set__(self, obj, id):
super().__set__(obj, id)
obj._id = id
def __get__(self, obj, objType=None):
return obj._id
def __del__(self):
pass
If you really need this level of abstraction, this is possibly the best way you can do it. My suggestion bellow can maybe save one line per class.
If you can afford to have attributes like "size" and "type" to be defined
on the final class, a richer base class and a declarative structure containing the checks as "lambda functions" can be used like this.
Note the usage of __init_subclass__ to check if all the parametes
needed for the guard expressions are defined:
from typing import Sequence
GUARDS = {
"typed": ((lambda self, value: "Incorrect type" if not instance(value, self._type) else None), ("_typed",)),
"positive": ((lambda self, value: "Only positive values" if value < 0 else None), ()),
"sized": ((lambda self, value: None if value <= 2 ** self.size - 1 else f"{value} must be smaller than 2**{self.size}"), ("size",)),
}
class DescriptorBase:
guards: Sequence[str]
def __init_subclass__(cls):
_sentinel = object()
for guard_name in cls.guards:
guard = GUARDS[guard_name]
required_attrs = guard[1]
missing = []
for attr in required_attrs:
if getattr(cls, attr, _sentinel) is _sentinel:
missing.append(attr)
if missing:
raise TypeError("Guarded descriptor {cls.__name__} did not declare required attrs: {missing}")
def __set_name__(self, owner, name):
self._name = f"_{name}""
def __set__(self, instance, value):
errors = []
for guard_name in self.guards:
if (error:= GUARDS[guard_name](self, value)) is not None:
errors.append(error)
if errors:
raise ValueError("\n".join(errors))
setattr (instance, self._name, value)
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.name)
def __del__(self, instance):
delattr(instance, self._name)
class Foo(DescriptorBase):
guards = ("typed", "positive", "sized")
size = 8
type_ = int
# No other code required here: __get__, __set__, __del__ handled in superclass
class UseAttr:
# Actual smart-attr usage:
my_foo = Foo()
Actually, if you want the class hierarchy, with less lines (no need to declare a __set__ method in each class), this approach can be used as well:
just change __init_superclass__ to collect "guards" in all superclasses,
and consolidate a single guards list on the class being defined, and then
define your composable guard-classes just as:
class Positive(BaseDescriptor):
guards = ("positive",)
class Sized(BaseDescriptor):
guards = ("sized",)
size = None
class Foo(Positive, Sized):
size = 8
class Fe(Foo):
name = "Fe name"
Actually, the change needed for this to work can be as simple as:
def __init_subclass__(cls):
_sentinel = object()
all_guards = []
for supercls in cls.__mro__:
all_guards.extend(getattr(supercls, "guards", ()))
# filter unique:
seem = {}
new_guards = []
for guard in all_guards:
if guard not in seem:
new_guards.append(guard)
seem.add(guard)
cls.guards = new_guards
for guard_name in cls.guards:
Also note that you could also collect the contents of the "GUARDS" registry from each defined class, instead of having to declare everything as lambdas before hand. I think you can get the idea from here on.

Correctly create a display name for attribute in Python

I'm trying to create a class that will have attributes which have a display name, i.e,
class MainClass:
def __init__(self, value):
self.ip24xs = Attribute(value = value, display_name="Attribute X")
This Attribute object I thought of implementing like a descriptor, but once I call for example mc.ip24xs.display_name I get that the str object has no attribute display_name, which makes sense, because it's the return value of the __get__ method. What is the correct way of implementing such functionality?
#dataclass
class Attribute:
value : Any = None
display_name : str = "var"
def __get__(self, obj, objtype = None):
print(f"Retrieving {self.display_name}")
return self.value
def __set__(self, obj, val):
print(f"Setting {self.display_name}")
self.value = val
I copy/pasted your code, and I don't get any errors. Only, ip24xs is not working as a descriptor but as a regular attribute or MainClass, because descriptors work in a different way. This will do, however:
#dataclass
class Attribute:
display_name : str
def __get__(self, obj, objtype = None):
print(f"Retrieving {self.display_name}")
return obj.value
def __set__(self, obj, val):
print(f"Setting {self.display_name}")
obj.value = val
class MainClass:
ip24xs = Attribute(display_name="Attribute X")
def __init__(self,value):
self.value = value
Note the difference: value is a regular attribute of MainClass, not of Attribute, so you reference it in __get__() and __set__() by obj.value, not self.value. You may also want to make it "private" as in _value just to make (more or less) sure it is not accessed directly

How to decorate a class and use descriptors to access properties?

I am trying to master (begin ;)) to understand how to properly work with decorators and descriptors on Python 3. I came up with an idea that i´m trying to figure how to code it.
I want to be able to create a class A decorated with certain "function" B or "class" B that allows me to create a instance of A, after delaring properties on A to be a component of certain type and assigning values on A __init__ magic function. For instance:
componentized is certain "function B" or "class B" that allows me to declarate a class Vector. I declare x and y to be a component(float) like this:
#componentized
class Vector:
x = component(float)
y = component(float)
def __init__ (self, x, y):
self.x = x
self.y = y
What I have in mind is to be able to this:
v = Vector(1,2)
v.x #returns 1
But the main goal is that I want do this for every marked component(float) property:
v.xy #returns a tuple (1,2)
v.xy = (3,4) #assigns to x the value 3 and y the value 4
My idea is to create a decorator #componentized that overrides the __getattr__ and __setattr__ magic methods. Sort of this:
def componentized(cls):
class Wrapper(object):
def __init__(self, *args):
self.wrapped = cls(*args)
def __getattr__(self, name):
print("Getting :", name)
if(len(name) == 1):
return getattr(self.wrapped, name)
t = []
for x in name:
t.append(getattr(self.wrapped, x))
return tuple(t)
#componentized
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y
And it kind of worked, but i don't think I quite understood what happened. Cause when I tried to do an assign and override the __setattr__ magic method it gets invoked even when I am instantiating the class. Two times in the following example:
vector = Vector(1,2)
vector.x = 1
How would could I achieve that sort of behavior?
Thanks in advance! If more info is needed don't hesitate to ask!
EDIT:
Following #Diego's answer I manage to do this:
def componentized(cls):
class wrappedClass(object):
def __init__(self, *args, **kwargs):
t = cls(*args,**kwargs)
self.wrappedInstance = t
def __getattr__(self, item):
if(len(item) == 1):
return self.wrappedInstance.__getattribute__(item)
else:
return tuple(self.wrappedInstance.__getattribute__(char) for char in item)
def __setattr__(self, attributeName, value):
if isinstance(value, tuple):
for char, val in zip(attributeName, value):
self.wrappedInstance.__setattr__(char, val)
elif isinstance(value, int): #EMPHASIS HERE
for char in attributeName:
self.wrappedInstance.__setattr__(char, value)
else:
object.__setattr__(self, attributeName, value)
return wrappedClass
And Having a class Vector like this:
#componentized
class Vector:
def __init__ (self, x, y):
self.x = x
self.y = y
It kind of behave like I wanted, but I still have no idea how to achieve:
x = component(float)
y = component(float)
inside the Vector class to somehow subscribe x and y of type float, so when I do the #EMPHASIS LINE(in the line I hardcoded a specific type) on the code I can check whether the value someone is assigning a value to x and/or y for an instance of Vector is of type I defined it with:
x = component(float)
So I tried this (a component (descriptor) class):
class component(object):
def __init__(self, t, initval=None):
self.val = initval
self.type = t
def __get__(self, obj, objtype):
return self.val
def __set__(self, obj, val):
self.val = val
To use component like a descriptor, but I couldn't managed to do a workaround to handle the type. I tried to do an array to hold val and type, but then didn't know how to get it from the decorator __setattr__ method.
Can you point me into the right direction?
PS: Hope you guys understand what I am trying to do and lend me a hand with it. Thanks in advance
Solution
Well, using #Diego´s answer (which I will be accepting) and some workarounds to achieve my personal needs I managed to this:
Decorator (componentized)
def componentized(cls):
class wrappedClass(object):
def __init__(self, *args):
self.wrappedInstance = cls(*args)
def __getattr__(self, name):
#Checking if we only request for a single char named value
#and return the value using getattr() for the wrappedInstance instance
#If not, then we return a tuple getting every wrappedInstance attribute
if(len(name) == 1):
return getattr(self.wrappedInstance, name)
else:
return tuple(getattr(self.wrappedInstance, char) for char in name)
def __setattr__(self, attributeName, value):
try:
#We check if there is not an instance created on the wrappedClass __dict__
#Meaning we are initializing the class
if len(self.__dict__) == 0:
self.__dict__[attributeName] = value
elif isinstance(value, tuple): # We get a Tuple assign
self.__checkMultipleAssign(attributeName)
for char, val in zip(attributeName, value):
setattr(self.wrappedInstance, char, val)
else:
#We get a value assign to every component
self.__checkMultipleAssign(attributeName)
for char in attributeName:
setattr(self.wrappedInstance, char, value)
except Exception as e:
print(e)
def __checkMultipleAssign(self, attributeName):
#With this we avoid assigning multiple values to the same property like this
# instance.xx = (2,3) => Exception
for i in range(0,len(attributeName)):
for j in range(i+1,len(attributeName)):
if attributeName[i] == attributeName[j]:
raise Exception("Multiple component assignment not allowed")
return wrappedClass
component (descriptor class)
class component(object):
def __init__(self, t):
self.type = t #We store the type
self.value = None #We set an initial value to None
def __get__(self, obj, objtype):
return self.value #Return the value
def __set__(self, obj, value):
try:
#We check whether the type of the component is diferent to the assigned value type and raise an exeption
if self.type != type(value):
raise Exception("Type \"{}\" do not match \"{}\".\n\t--Assignation never happened".format(type(value), self.type))
except Exception as e:
print(e)
else:
#If the type match we set the value
self.value = value
(The code comments are self explanatories)
With this design I can achieve what I wanted (explained above)
Thanks you all for your help.
I thing there is an easiest way to achive the behavior : overloading __getattr__and __setattr__ functions.
Getting vector.xy :
class Vector:
...
def __getattr__(self, item):
return tuple(object.__getattribute__(self, char) for char in item)
The __getattr__ function is called only when "normal" ways of accessing an atribute fails, as stated in the Python documentation.
So, when python doesn't find vector.xy, the __getattr__method is called and we return a tuple of every value (ie. x and y).
We use object.__getattribute__ to avoid infinite recurtion.
Setting vector.abc :
def __setattr__(self, key, value):
if isinstance(value, tuple) and len(key) == len(value):
for char, val in zip(key, value):
object.__setattr__(self, char, val)
else:
object.__setattr__(self, key, value)
The __setattr__ method is always called unlike __getattr__, so we set each value separately only when the item we want to set is of the same lenght as the tuple of value.
>>> vector = Vector(4, 2)
>>> vector.x
4
>>> vector.xy
(4, 2)
>>> vector.xyz = 1, 2, 3
>>> vector.xyxyxyzzz
(1, 2, 1, 2, 1, 2, 3, 3, 3)
The only drawback is that if you really want to asign a tuple like (suppose you have an attribute called size):
vector.size = (1, 2, 3, 4)
Then s, i, z and e will by assigned separately, and that's obviously not what you want !
FWIW, I've done something similar by abusing __slots__. I created an Abstract Base Class which read the subclass's slots and then used that for pickling (with __getstate__ and __setstate__). You could do something similar with get-set-attr but you will still need to muck about with the class's actual attr's vs the ones you want to use as get/set properties.
Previous answer:
Why not just use the #property decorator? See the third example in the docs. You would apply it by first changing the attr names to something different and private (like _x) and then use the actual name x as the property.
class Vector(object):
def __init__(self, x, y):
self._x = x
self._y = y
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#property
def xy(self):
return (self._x, self._y) # returns a tuple
#xy.setter
def xy(self, value):
self._x, self._y = value # splits out `value` to _x and _y
And if you want this to happen with every attr automatically, then you will need to use a metaclass, as #kasramvd commented. If you don't have many such different classes where you want to do this or many properties, may not be worth the effort.

Polymorphic class in Python that returns an instance of another class

I have class container that transmute itself into another class.
For example I have some types such as MyFloat MyStr or MyInt that offer additional methods or attributes. I would like to encapsulate the decision to build any of these types into another class:
My first attempt was to write this:
class MyFloat(float):
def foo_float():
pass
class MyStr(str):
def foo_str():
pass
class MyInt(int):
def foo_int():
pass
# Does not work
class Polymorph(object):
def __init__(self, value):
if isinstance(value, float):
self = MyFloat(value)
elif isinstance(value, int):
self = MyInt(value)
elif isinstance(value, str):
self = MyStr(value)
else:
raise TypeError, 'Unknown type'
Unfortunately I did not get the expected instance at the end:
>>> a = Polymorph(42.42) # Should get an instance of MyFloat
>>> type(a)
__main.MyFloat
I then tried to use __new__ instead
class Polymorph(object):
def __new__(cls, value):
if isinstance(value, float):
return super(MyFloat, cls).__new__(cls, value)
elif isinstance(value, int):
return super(MyInt, cls).__new__(cls, value)
elif isinstance(value, str):
return super(MyStr, cls).__new__(cls, value)
else:
raise TypeError, 'Unknown type'
But this time I get a TypeError: super(type, obj): obj must be an instance or subtype of type
Is it possible to achieve this?
So I found this solution that works. However, I don't know is it is Pythonic Acceptable to do this.
class Polymorph(object):
def __new__(cls, value):
if isinstance(value, float):
return MyFloat(value)
elif isinstance(value, int):
return MyInt(value)
elif isinstance(value, str):
return MyStr(value)
else:
raise TypeError, 'Unknown type'

Categories