Pojo like classes in Python - python

I want to create a Pojo like class for User in Python. Each of the property will involve some validations. For example: I don't want someone to create a User with a negative value of age. To acheive that I will end up writing a class something like below.
class User:
def __init__(self, age):
self.age = age
#property
def age(self):
return self._age
#age.setter
def age(self, value):
print("Called")
if value >= 0:
self._age = 0
else:
raise ValueError("Age can't be negative")
I am bit scared after seeing the class above. Reasons:
My User class will have somewhere around 50'ish properties. I am bit scared to imagine how big this class will look in that case.
Is this a correct way of creating such classes in Python?

I don't see what's clumpsy about this - most languages out there do not have built-in getter/setter validation so you would end up writing something similar to this in any language you choose - if you want to turn all your properties into getter/setter pairs with validation, you'll have to write them separately.
Now, Python is a dynamic language of a very flexible nature so there are several ways which you can use to reduce the verbosity (but not the complexity) of this process. For example, you can create your own validator decorator to be clamped on top of your setters, e.g.:
def validator(cmp, exc):
def decorator(setter): # a decorator for the setter
def wrapper(self, value): # the wrapper around your setter
if cmp(value): # if cmp function returns True, raise the passed exception
raise exc
setter(self, value) # otherwise just call the setter to do its job
return wrapper
return decorator
And now you can define your getter/setter pairs with validation included as:
class User:
def __init__(self, age):
self.age = age
#property
def age(self):
return self._age
#age.setter
#validator(lambda x: x < 0, ValueError("Age can't be negative"))
def age(self, value):
self._age = value
However, if you're only ever going to just do the validation and no other processing in your setters (and getters), you can just define your own validating property and save a lot on verbosity, something like:
class ValidatingProperty(object):
def __init__(self, prop, cmp, exc):
self.prop = prop
self.cmp = cmp
self.exc = exc
def __get__(self, instance, owner=None):
if instance is None:
return self
return getattr(instance, self.prop, None)
def __set__(self, instance, value):
if self.cmp(value):
raise self.exc
setattr(instance, self.prop, value)
def __delete__(self, instance):
delattr(instance, self.prop)
And now you can build your class getters/setters as simple as:
class User:
age = ValidatingProperty("_age", lambda x: x < 0, ValueError("Age can't be negative"))
def __init__(self, age):
self.age = age
And if you ever need to access the raw property (assuming it was set), without wrappers around it, you can still access it with self._age (or whatever 'real' property that you've passed as the first argument to ValidatingProperty). You can even build your validators separately so you don't rely on lambdas (e.g. create an IntegerValidator class which lets you pass the ranges for validation and then reuse where needed).
The other option is to treat the users of your classes as adults and explain the valid values in the documentation and if they go outside of that - there be dragons. If the class is intended to be populated with data from end-users, the validation should be performed on the side that collects the end-user data (so that the end users can get a meaningful error with them in mind), not necessarily in the model itself.

Related

does the #property decorator function as a getter?

i am new to python and i'm trying to understand the use of the 'getter'. it's use case is not obvious to me.
if i use a property decorator on a method and im able to return a certain value, what exactly would i use 'getter' for.
class Person:
def __init__(self,name, age):
self._name = name
self._age = age
#property
def age(self):
return self._age
#age.setter
def age(self,new_age):
if isinstance(new_age,int) and 18 < new_age < 120:
self._age = new_age
The #property decorator adds a default getter on a given field in a Python class
that triggers a function call upon accessing a property.
The #property decorator turns the age() method into a “getter” for a read-only attribute with the same name. If want a “setter” then add #age.setter as you did in your question.
p = Person("John", 22)
print(p.age)
Output:
22
The property type can take up to 4 separate arguments (a getter, a setter, a deleter, and a doc string) when instantiating it. The first argument is a function that will be used as a getter. The second is a function that will be used as a setter. You could have written your class as
class Person:
def __init__(self,name, age):
self._name = name
self._age = age
def _age_getter(self):
return self._age
def _age_setter(self, new_age):
...
age = property(_age_getter, _age_setter)
This is cumbersome to write, so property objects have a number of methods for building the property up piece by piece. Using property as a simple decorator creates a read-only property with the decorated function as the getter.
# Equivalent to
# def age(self):
# return self._age
# age = property(age)
#property
def age(self):
return self._age
age.setter is a method of the property instance which creates a new property that is essentially a copy of age, but with its argument used to replace whatever setter the original property had. (Decorator syntax is why all the methods involved have to have the same name, so that we are constantly replacing the original property with the new, augmented property, rather than defining multiple similar properties with different names instead.)
#age.setter
def age(self, new_age):
...
Desugaring this requires the use of a temporary variable, to avoid losing the old value of age prematurely.
old_property = age
def age(self, new_age):
...
age = old_property.setter(age)
A silly, but legal, way of defining the property takes advantage of the fact that property can be defined with no arguments, and that there is a getter method that can be used as a decorator as well. (I don't think I've ever seen it used in the wild, though.)
class Person:
def __init__(self, name, age):
...
age = property()
#age.getter
def age(self):
...
#age.setter
def age(self, new_age):
...
Note that the order in which we use getter, setter, (and deleter) methods doesn't matter. The conventional order comes from the order in which property expects its positional arguments to be supplied, as well as the fact that new properties are virtually always defined by decorating the getter directly, rather than adding a getter to a property after it is created.

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

Modifying Function Arguments in Python to Access Private Fields Within a Class

I'm trying to create a Class with a list of fields. See code below.
class Character:
# Private Fields:
__age = 18
__weight = 200
__height = 72
def __init__(self, name):
self.__name = name
#property
def get_age(self):
return self.__age
#property
def get_name(self):
return self.__name
#property
def get_weight(self):
return self.__weight
#property
def get_height(self):
return self.__height
person = Character("someone")
print("name =", person.get_name,",", "age =", person.get_age)
Is there a way to avoid writing the #property for every private field you want to access? For instance is there a way to pass an attribute into a more general getter function like:
def get_attr(self,attr):
#set attr to __attr
#return self.attr
I tried using the join function, but it didn't work
Thanks for any help
To answer the question as asked, the simple solution is to compensate for the name mangling that is done with private members. e.g. to get the __age attribute you'd use: person._Character__age
But, this would be a terrible idea and I wouldn't recommend it. If you need them to be easily accessible, just remove the underscores. If they really need to be private, they shouldn't be easily accessible from outside the class anyway, so putting in a way to make them accessible defeats the purpose.

How to specify Python class attributes with constructor keyword arguments

I'm trying to come up with a way to allow specification of any number of class attributes upon instantiation, very similar to a dictionary. Ideal use case:
>>> instance = BlankStruct(spam=0, eggs=1)
>>> instance.spam
0
>>> instance.eggs
1
where BlankStruct is defined as:
class BlankStruct(Specifiable):
#Specifiable.specifiable
def __init__(self, **kwargs):
pass
I was thinking of using a parent class decorator, but am lost in a mind-trip about whether to use instance methods, class methods or static methods (or possibly none of the above!). This is the best I've come up with so far, but the problem is that the attributes are applied to the class instead of the instance:
class Specifiable:
#classmethod
def specifiable(cls, constructor):
def constructor_wrapper(*args, **kwargs):
constructor(*args, **kwargs)
cls.set_attrs(**kwargs)
return constructor_wrapper
#classmethod
def set_attrs(cls, **kwargs):
for key in kwargs:
setattr(cls, key, kwargs[key])
How can I make such a parent class?
NOTE:
Yes, I know what I'm trying to do is bad practice. But sometimes you just have to do what your boss tells you.
Yes, you can do the following, however I do NOT recommend as this clearly goes against the explicit is better than implicit principle:
class BlankStruct:
def __init__(self, **attrs):
self.__dict__.update(**attrs)
def __getattr__(self, attr):
return self.__dict__.get(attr, None)
f = BlankStruct(spam=0, eggs=1)
A more complete response is available here and inspired this answer.
I would recommend being explicit in terms of the properties you want your class to have. Otherwise, you are left with a class that has a high degree of variability, which likely detracts for it's usefulness.
I believe you can do this without decorators:
class Specifiable:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
class BlankStruct(Specifiable):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Do other stuff.

python: get constructor to return an existing object instead of a new one

I have a class that knows its existing instances. Sometimes I want the class constructor to return an existing object instead of creating a new one.
class X:
def __new__(cls, arg):
i = f(arg)
if i:
return X._registry[i]
else:
return object.__new__(cls)
# more stuff here (such as __init_, _registry, etc.)
Of course, if the first branch is executed, I don't need __init__, but it's invoked anyways. What's a good way to tell __init__ to do nothing?
I can probably just add some attribute to keep track of whether __init__ has run yet, but perhaps there's a better way?
In languages that support private constructors (C#, Dart, Scala, etc), factory methods provide a robust solution to this problem.
In Python, however, class constructors are always accessible, and so a user of your class may easily forget the factory method and call the constructor directly, producing duplicate copies of objects that should be unique.
A fool-proof solution to this problem can be achieved using a metaclass. The example below assumes that the zeroth constructor argument can be used to uniquely identify each instance:
class Unique(type):
def __call__(cls, *args, **kwargs):
if args[0] not in cls._cache:
self = cls.__new__(cls, *args, **kwargs)
cls.__init__(self, *args, **kwargs)
cls._cache[args[0]] = self
return cls._cache[args[0]]
def __init__(cls, name, bases, attributes):
super().__init__(name, bases, attributes)
cls._cache = {}
It can be used as follows:
class Country(metaclass=Unique):
def __init__(self, name: str, population: float, nationalDish: str):
self.name = name
self.population = population
self.nationalDish = nationalDish
placeA = Country("Netherlands", 16.8e6, "Stamppot")
placeB = Country("Yemen", 24.41e6, "Saltah")
placeC = Country("Netherlands", 11, "Children's tears")
print(placeA is placeB) # -> False
print(placeA is placeC) # -> True
print(placeC.nationalDish) # -> Stamppot
In summary, this approach is useful if you want to produce a set of unique objects at runtime (possibly using data in which entries may be repeated).
Use a factory, i.e.
_x_singleton = None
def XFactory():
global _x_singleton
if _x_singleton is None:
_x_singleton = X()
return _x_singleton
or use a "create" classmethod in your class that behaves the way you want it to,
class X(object):
instance = None
def __init__(self):
# ...
#classmethod
def create(cls):
if cls.instance is None:
cls.instance = cls()
return cls.instance
You might even consider making __init__ raise an exception if some condition isn't met (i.e. self.instance not being None)

Categories