Python class attribute validation on __init__ - python

I'm trying to validate one attribute of my class using setter in the code below. The attribute I want to validate is called '__x' and is set to parameter passed on 'init' method. When I change 'self__x' to 'self.x', it's working as I expect. What I want to is how it's working with 'self.x', while I don't return 'x' attribute anywhere in getter and setter methods and why it's not working with 'self.__x'?
class P:
def __init__(self, x):
self.__x = x # not working
# self.x = x # working
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
else:
self.__x = x
p = P(-5)
print(p.x) # prints -5

It's like this. Imagine there's a school bully, let's call him Dan, who targets you. There's also Beth, who you like very much. Normally, you want to avoid Dan and meet Beth, but Dan doesn't care and will bop you on the head if he sees you.
Now you also make friends with Joe. He's a gentle giant. Very nice guy. He says to come to his place and he'll make sure he doesn't let Dan in. It all works great: when Dan comes to Joe's door, he's turned away; when Beth comes, Joe lets her in.
The key point is this: it only works as long as Dan is opening the door. If you hear the doorbell and you go out yourself, it doesn't work any more.
So here, if you do self.x = -5, Joe checks the number, sees it's Dan, and sends him packing with a zero. But if you do self.__x = -5, Joe never sees Dan. You get a bop on the head.
self.__x is just a variable, it can't do any checking on its own. self.x is a function though (two of them really, one for reading and one for writing), and it can do whatever it wants - set self.__x or refuse to.

Let's begin with the "#decorator" syntax. It's actually only syntactic sugar, so
#decorate
def myfunc():
pass
is just a shorthand for
def myfunc():
pass
myfunc = decorate(myfunc)
Note that python functions are objects too (as well as classes and modules FWIW) so you can pass functions as arguments to other functions, return functions from functions, store functions as variables or attributes etc.
Now with the property class (yes, it's a class): it's only a generic implementation of the descriptor protocol, which is the python mechanism to support computed attributes.
A naive python implementation of property would mostly look something like (I ignore the fdel and __del__ parts):
class propertytype(type):
# this is what will allow you
# to use `property` as decorator,
# it will return a new `property` instance
# with `func` as setter
def __call__(cls, func):
return cls(func)
class property(metaclass=propertytype):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
# this is the getter
def __get__(self, instance, cls=None):
if instance is None:
return self
return self.fget(instance)
# this is the setter (if there's one)
def __set__(self, instance, value):
if not self.fset:
raise AttributeError("Attribute is read-only")
self.fset(instance, value)
# and this allows you to use`#myprop.setter`
# in your class definition
def setter(self, func):
self.fset = func
return self
And finally: while it's good practice to create all instance attributes of an object in the initializer (the __init__ method), you can actually set existing or new attributes just wherever and whenever you want. Except for a few types that (mainly for implementation reasons) use a totally different way to store attributes (you can look for slots if you want to learn more about this), ordinary Python objects are, mainly, dicts in disguise, so myobj.foo = 'bar' will usually just store 'bar' in self.__dict__['foo']. Well, if you don't use computed attributes, of course ;)
Ok, now we have the building blocks, let analyze what's going on with your class:
class P:
# let's ignore the initializer for now
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
else:
self.__x = x
This could be rewritten as
class P:
# let's ignore the initializer for now
def _getx(self):
return self.__x
def _setx(self):
if x < 0:
self.__x = 0
else:
self.__x = x
x = property(_getx, setx)
So now with
p = P()
when we do:
p.x = 5
the attribute resolution rules (implemented in object.__setattr__(self, name, value)) will actually lookup "x" on "P", find our "x" property, and since it's a binding descriptor (it has a __set__ method), call x.__set__(p, 5), which in turn will call self.fset(p, 5) (cf property.__set__() definition), which will call p._setx(5).
And if we had back the initializer:
class P:
def __init__(self, x):
self.x = x
# getter / setter / property definition here
then the very exact thing happens (except the P instance is named self instead of p ) - it actually ends up calling P._setx(self, x).
The only difference with your original implementation is that using the property has a decorator, the getter and setter functions do not become methods of the class, they only live as the fget and fset attributes of the x property object.

Related

Python - Descriptor - Broken class

I follow the tutorial out of the docs and an example by fluent Python. In the book they teach me how to avoid the AttributeError by get, (e.g., when you do z = Testing.x) and I wanted to do something simliar with the set method. But it seems like, it lead to a broken class with no error.
To be more specific about the issue:
With outcommented line Testing.x = 1 it invoke the __set__ methods.
With uncommented line #Testing.x = 1 it does not invoke the __set__ methods.
Can someone teach me why it behaves this way?
import abc
class Descriptor:
def __init__(self):
cls = self.__class__
self.storage_name = cls.__name__
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.storage_name)
def __set__(self, instance, value):
print(instance,self.storage_name)
setattr(instance, self.storage_name, value)
class Validator(Descriptor):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
#abc.abstractmethod
def validate(self, instance, value):
"""return validated value or raise ValueError"""
class NonNegative(Validator):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{value!r} must be > 0')
return value
class Testing:
x = NonNegative()
def __init__(self,number):
self.x = number
#Testing.x = 1
t = Testing(1)
t.x = 1
Attribute access is generally handled by object.__getattribute__ and type.__getattribute__ (for instances of type, i.e. classes). When an attribute lookup of the form a.x involves a descriptor as x, then various binding rules come into effect, based on what x is:
Instance binding: If binding to an object instance, a.x is transformed into the call: type(a).__dict__['x'].__get__(a, type(a)).
Class binding: If binding to a class, A.x is transformed into the call: A.__dict__['x'].__get__(None, A).
Super binding: [...]
For the scope of this question, only (2) is relevant. Here, Testing.x invokes the descriptor via __get__(None, Testing). Now one might ask why this is done instead of simply returning the descriptor object itself (as if it was any other object, say an int). This behavior is useful to implement the classmethod decorator. The descriptor HowTo guide provides an example implementation:
class ClassMethod:
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
print(f'{obj = }, {cls = }')
return self.f.__get__(cls, cls) # simplified version
class Test:
#ClassMethod
def func(cls, x):
pass
Test().func(2) # call from instance
Test.func(1) # this requires binding without any instance
We can observe that for the second case Test.func(1) there is no instance involved, but the ClassMethod descriptor can still bind to the cls.
Given that __get__ is used for both, instance and class binding, one might ask why this isn't the case for __set__. Specifically, for x.y = z, if y is a data descriptor, why doesn't it invoke y.__set__(None, z)? I guess the reason is that there is no good use case for that and it unnecessarily complicates the descriptor API. What would the descriptor do with that information anyway? Typically, managing how attributes are set is done by the class (or metaclass for types), via object.__setattr__ or type.__setattr__.
So to prevent Testing.x from being replaced by a user, you could use a custom metaclass:
class ProtectDataDescriptors(type):
def __setattr__(self, name, value):
if hasattr(getattr(self, name, None), '__set__'):
raise AttributeError(f'Cannot override data descriptor {name!r}')
super().__setattr__(name, value)
class Testing(metaclass=ProtectDataDescriptors):
x = NonNegative()
def __init__(self, number):
self.x = number
Testing.x = 1 # now this raises AttributeError
However, this is not an absolute guarantee as users can still use type.__setattr__ directly to override that attribute:
type.__setattr__(Testing, 'x', 1) # this will bypass ProtectDataDescriptors.__setattr__
The line
Testing.x = 1
replaces the descriptor you've set as a class attribute for Testing with an integer.
Since the descriptor is no more, self.x = ... or t.x = ... is just an assignment that doesn't involve a descriptor.
As an aside, surely you've noticed there is no true x attribute anymore with your descriptor, and you can't use more than one instance of the same descriptor without conflicts?
class Testing:
x = NonNegative()
y = NonNegative()
def __init__(self, number):
self.x = number
t = Testing(2345)
t.x = 1234
t.y = 5678
print(vars(t))
prints out
{'NonNegative': 5678}

Replacing the object from one of its methods

I am using python and have an object, that object has a method. I am looking for a simple way, to replace the entire object from within that function.
E.g
class a():
def b(self):
self = other_object
How can you do that?
Thanks
You use a proxy/facade object to hold a reference to the actual object, the self if you wish and that proxy (better term than Facade, but not changing my code now) is what the rest of your codebase sees. However, any attribute/method access is forwarded on to the actual object, which is swappable.
Code below should give you a rough idea. Note that you need to be careful about recursion around __the_instance, which is why I am assigning to __dict__ directly. Bit messy, since it's been a while I've written code that wraps getattr and setattr entirely.
class Facade:
def __init__(self, instance):
self.set_obj(instance)
def set_obj(self, instance):
self.__dict__["__theinstance"] = instance
def __getattr__(self, attrname):
if attrname == "__theinstance":
return self.__dict__["__theinstance"]
return getattr(self.__dict__["__theinstance"], attrname)
def __setattr__(self, attrname, value):
if attrname == "__theinstance":
self.set_obj(value)
return setattr(self.__dict__["__theinstance"], attrname, value)
class Test:
def __init__(self, name, cntr):
self.name = name
self.cntr = cntr
def __repr__(self):
return "%s[%s]" % (self.__class__.__name__, self.__dict__)
obj1 = Test("first object", 1)
obj2 = Test("second", 2)
obj2.message = "greetings"
def pretend_client_code(facade):
print(id(facade), facade.name, facade.cntr, getattr(facade, "value", None))
facade = Facade(obj1)
pretend_client_code(facade)
facade.set_obj(obj2)
pretend_client_code(facade)
facade.value = 3
pretend_client_code(facade)
facade.set_obj(obj1)
pretend_client_code(facade)
output:
4467187104 first object 1 None
4467187104 second 2 None
4467187104 second 2 3
4467187104 first object 1 None
So basically, the "client code" always sees the same facade object, but what it is actually accessing depends on what your equivalent of def b is has done.
Facade has a specific meaning in Design Patterns terminology and it may not be really applicable here, but close enough. Maybe Proxy would have been better.
Note that if you want to change the class on the same object, that is a different thing, done through assigning self.__class__ . For example, say an RPG game with an EnemyClass who gets swapped to DeadEnemyClass once killed: self.__class__ = DeadEnemyClass
You can't directly do that. What you can do is save it as an instance variable.
class A():
def __init__(self, instance=None):
self.instance = val or self
# yes, you can make it a property as well.
def set_val(self, obj):
self.instance = obj
def get_val(self):
return self.instance
It is unlikely that replacing the 'self' variable will accomplish
whatever you're trying to do, that couldn't just be accomplished by
storing the result of func(self) in a different variable. 'self' is
effectively a local variable only defined for the duration of the
method call, used to pass in the instance of the class which is being
operated upon. Replacing self will not actually replace references to
the original instance of the class held by other objects, nor will it
create a lasting reference to the new instance which was assigned to
it.
Original source: Is it safe to replace a self object by another object of the same type in a method?

The pythonic way to construct a multimethod setter

We can use a #property to construct a getter and setter. This is a short example how we can do this:
class A:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 100:
self.__x = 100
else:
self.__x = x
My case seems to be more complicated.
class A:
def __init__(self, x):
self.__x = x
self.x1()
self.x2()
self.x3()
def x1(self):
self.__x1 = self.__x + 1
return self.__x1
def x2(self):
self.__x2 = self.__x1 + 2
return self.__x2
def x3(self):
self.__x3 = self.__x2 + 3
return self.__x3
if __name__ == "__main__":
a = A(3)
print(a.x3)
Methods x1, x2 and x3 are oversimplified. The self.__x3 variable is set only once, when the __init__ method is called. Now, I need a getter method to get self.__x3 by calling a.x3. How to achieve that in the pythonic way?
Attempting an answer based on the assumption that you want the __x# variables modified only during __init__, and never again, but also want the accessors to follow the same code path (possibly because the read is also programmatically complex):
In this case, you can have the implementing function take an additional, defaulted argument. When accessed in attribute form, it will receive the defaulted argument, but if the fget member of the property is explicitly accessed, it can be called with the non-default argument. A simple example addressing x1 only:
class A:
def __init__(self, x):
self.__x = x
# Access the property itself off the class, bypassing execution,
# then call it directly with the non-default argument
type(self).x1.fget(self, True)
#property
def x1(self, doset=False):
if doset:
self.__x1 = self.__x + 1
return self.__x1
Alternatively, to simplify the usage in __init__, you can use a separate name for the underlying function vs. the property to achieve the same effect:
class A:
def __init__(self, x):
self.__x = x
# Call the implementing function directly with the non-default argument
self._x1(True)
# Implementing function named with single underscore prefix to indicate it's
# for internal/protected use only
def _x1(self, doset=False):
if doset:
self.__x1 = self.__x + 1
return self.__x1
# Define property x1 based on x1 for outside use
x1 = property(_x1)
Of course, if you don't have a complicated getter path, then the real solution is to separate _x1 from x1 completely, where _x1 is pure setter helper function for __init__, and x1 is pure getter:
class A:
def __init__(self, x):
self.__x = x
# Call the init helper
self._init_x1()
# Implementing function named with single underscore prefix to indicate it's
# for internal/protected use only
def _init_x1(self):
self.__x1 = self.__x + 1
#property:
def x1(self):
return self.__x1
To be clear, only the last of these is "Pythonic" in any meaningful sense. The second option has some limited use cases (where you have a function that demands existence, and is highly configurable, but has a reasonable set of defaults that a property could use), but in that case, it's usually a function that has public utility just like the property. Option #1 is the least Pythonic, as it's inconvenient to use (needing to elevate to the class type, extract the fget member, and explicitly pass self), and makes it quite clear that there is no expected use case outside of __init__ (because it's such a pain to use that no one would bother).

Python Getters, Setters

class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
I am trying to learn 'getters' and 'setters' methods.
When I create an instance with these inputs:
p1 = P(1001)
print(p1.x)
p1.x = -12
print(p1.x)
I get as outputs:
1001
-12
I don't know why do I get 1001 and -12 instead of 1000 and 0.
Your real problem is that you're using Python 2.x with old-style classes. Your code as shown will work as you expect on Python 3.x (except for the broken indentation, of course). It will also work on Python 2.x if you make P a new-style class by explicitly inheriting from object (e.g. declare it as class P(object):).
In an old-style class, the problem is that your setter and getter methods are both named x, and both are competing for entries in the same namespace.
When you have code like:
#x.setter
def x(self, x):
...
What happens is that a new function object (confusingly, named x) is created with the code you specify, then x.setter(x_function_object) is called, and whatever x.setter returns (which will be a descriptor object with __get__ and __set__ defined) is assigned to x in the namespace dictionary that is passed to the metaclass's __new__ method and used to build the class's type.
However, in an old-style class, when you write self.x = ... then it will invoke __setattr__ directly rather than using the descriptor protocol (self.__dict__['x'].__set__(x, ...)). Thus the assignment to self.x in your __init__ method will overwrite the setter method instead of calling it.

Can you change a python descriptor's __get__ method at runtime?

All,
As the title asks, is it possible to change the __get__ method of a Descriptor at run time. I'm in a situation where I've got a function that is being decorated and undecorated on the the fly at run time. I'd like the result of this function to be available as a attribute, similar to what the #property does. I researched that and found it's a descriptor, but it seems descriptors' __get__ method is read only.
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#property
def x( self ):
return self.get_x()
The above code does what I want, roughly, in that
The value is set in the constructor
I can decorate the get_x method to my heart's content
instance.x returns the correct value
My issue is that I'd rather not have to create the get_x method since it's basically unnecessary. I just haven't been able to decorate the __get__ method of x as it is read-only.
Background
I'm writing a turn based strategy game, and I'm using decorators to implement persistent conditions. I'm able to implement these decorators effectively when I use test cases, but the issue is that to get the computed value then, you must use a function call, not an attribute access. This seems like an bad idea because getting values describing a unit would inconsistently use functions or attributes. I'd like to standardize on attributes if I can.
You can override default "read-only" characteristic of property's __get__ attribute using simple inheritance :
class MyProperty( property ): pass
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#MyProperty
def x( self ):
return self.get_x()
test = Test()
The problem now that even if you redefine __get__ attribure of your Text.x property, on test.x request python runtime will call MyProperty.__get__(Test.x, test, Test)
So you could rewrite it only there like
MyProperty.__get__ = lambda self,instance,owner: ""the x attribute"
So good option here is to delegate call to some redifineable attribute like
MyProperty.__get__ = lambda self,instance,owner: self.get(instance,owner)
From now on get attribute of your property in your full control.
Also there is bad option to generate separate type for each property-like object.
So in good case you could do something like:
class MyProperty( property ):
def __get__(self,instance,owner) :
if not instance: return self
else: return self.get(instance,owner)
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#MyProperty
def x( self ): pass
#MyProperty
def y(self): pass
x.get = lambda self,clazz: self.get_x()
y.get = lambda self,clazz: "the y property of " + clazz.__name__ + " object"
>>> test = Test()
>>> test.x
10
>>> test.y
'the y property of Test object'

Categories

Resources