I want a callback to get called whenever a certain attribute of object A is changed.
I'm aware that this question is related to Observer Pattern and descriptors in Python. However, it seems descriptors could only detect explicit changes via dot access.
For instance:
class Observer(object):
def __init__(self, callback=None):
self.__callback = callback
def __set_name__(self, owner, name):
self.__name = name
def __set__(self, obj, value):
obj.__dict__[self.__name] = value
self.trigger()
def __get__(self, obj, type=None):
return obj.__dict__.get(self.__name)
def trigger(self):
self.__callback()
def hello():
print('hello')
class MyClass:
data = Observer(hello)
a = MyClass()
a.data = [[1],2,3]
a.data.append(4)
a.data[0][0] = -1
In the above code, the callback is only called once for the initialization of the data. However, I want it to be called 3 times. I'm not tied to using descriptors but I do want the method to work on any data types, such as list, dict and etc.
Related
Here in this code I am just calling out my static method, but it prints my instance variable too. Could you please explain the reason for that, and how to avoid them being printed?
Like below:
I am a static Method
None
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
print("I am a static Method")
p1 = Player()
print(p1.demo())
As Python docs says:
Print objects to the text stream file, separated by sep and followed
by end. sep, end, file, and flush, if present, must be given as
keyword arguments.
So you can return your message in method and then just print it:
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
return "I am a static Method"
p1 = Player()
print(p1.demo())
What is the correct way to implement a data descriptor inside a metaclass? In the following (trivial) example, I wish to always append a question mark to the desired value before setting it:
class AddQDesc:
def __init__ (self, name):
self.name = name
def __get__ (self, instance, owner=None):
obj = instance if instance != None else owner
return obj.__dict__[self.name]
def __set__ (self, instance, value):
# What should go here ?
#setattr(instance, self.name, "{}?".format(value)) <- This gives me recursion error
#instance.__dict__[self.name] = "{}?".format(value) <- This gives me proxymapping error
pass
class Meta (type):
var = AddQDesc("var")
class C (metaclass=Meta):
var = 5
print(C.var)
C.var = 1
print(C.var)
First, it looks like the descriptor was not used when I initialized var to 5. Can I somehow apply descriptor protocol here as well? (Make it "5?")
Second, how should the value be updated in the __set__ method? Updating the __dict__ gives me "TypeError: 'mappingproxy' object does not support item assignment" and using setattr gives me "RecursionError: maximum recursion depth exceeded while calling a Python object".
As I commented in the question, this is tricky - because there is no way from Python code to change a class' __dict__ attribute directly - one have to call setattr and let Python set a class attribute - and, setattr will "see" the descriptor in the metaclass, and call its __set__ instead of modifying the value in the class __dict__ itself. So, you get an infinite recursion loop.
Therefore, if you really require that the attribute proxied by the descriptor will "live" with the same name in the class'dict, you have to resort to: when setting the value, temporarily remove the descriptor from the metaclass, call setattr to set the value, and then restoring it back.
Also, if you want the values set in the class body to be handled
through the descriptor, they have to be set with setattr after the
class is created - type.__new__ won't check for the descriptor
as it builds the initial class __dict__.
from threading import Lock
class AddQDesc:
def __init__ (self, name):
self.name = name
self.lock = Lock()
def __get__ (self, instance, owner=None):
obj = instance if instance != None else owner
return obj.__dict__[self.name]
def __set__ (self, instance, value):
owner_metaclass = type(instance)
with self.lock:
# Temporarily remove the descriptor to avoid recursion problems
try:
# Note that a metaclass-inheritance hierarchy, where
# the descriptor might be placed in a superclass
# of the instance's metaclass, is not handled here.
delattr(owner_metaclass, self.name)
setattr(instance, self.name, value + 1)
finally:
setattr(owner_metaclass, self.name, self)
class Meta (type):
def __new__(mcls, name, bases, namespace):
post_init = {}
for key, value in list(namespace.items()):
if isinstance(getattr(mcls, key, None), AddQDesc):
post_init[key] = value
del namespace[key]
cls = super().__new__(mcls, name, bases, namespace)
for key, value in post_init.items():
setattr(cls, key, value)
return cls
var = AddQDesc("var")
class C (metaclass=Meta):
var = 5
print(C.var)
C.var = 1
print(C.var)
If you don't need the value to live in the class' __dict__, I'd suggest just storing it elsewhere - a dictionary in the descriptor instance for example, having the classes as keys, will suffice - and will be far less weird.
class AddQDesc:
def __init__ (self, name):
self.name = name
self.storage = {}
def __get__ (self, instance, owner):
if not instance: return self
return self.storage[instance]
def __set__ (self, instance, value):
self.storage[instance] = value + 1
I am trying to assign dictionary keys to object functions but for some reason it won't work inside of decorators. When I try to call a.run(), self doesn't seem to be passed into the dictionary func. I also don't have access to f.self in decorator so I know it has to be something wrong in there. I have written a simple example of my code. I want it to be something similar to app.route in flask being that it init the mapping between endpoints and functions.
ERROR:
Traceback (most recent call last):
File "main.py", line 27, in <module>
a.run()
File "main.py", line 14, in run
self.rmap[k](data)
TypeError: one_way() missing 1 required positional argument: 'data'
CODE:
class A (object):
def __init__(self):
self.rmap = {}
def route(self, r):
def decorator(f):
self.rmap[r] = f
return f
return decorator
def run(self):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](data)
a = A()
class B (object):
def __init__(self):
pass
#a.route('/one/way')
def one_way (self, data):
print('A WAY:{}'.format(self))
b = B()
a.run()
At the time it's being decorated, one_way() is a plain function, not a method - it only becomes a method when looked up on a B instance. IOW, you need to explicitely provide a B instance when calling it from A().run() (the fact you have a global b instance in your code is irrelevant - the function object stored in a.rmap knows absolutely nothing about it, nor even about the B class FWIW.
To make a long story short, your current design cannot work as is. If you only ever intend to decorate methods (well, functions) from one single class and call them on one single instance of this class, you could pass an instance of this class to a.run() ie:
class A():
# ...
def run(self, obj):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](obj, data)
b = B()
a.run(b)
but this would be of very limited use.
Or you could just use the decorator to "mark" functions to be used for routing (together with the effective route), add some register() methdo to A and explicitely pass B or whatever else instance to this method ie
def route(r):
def decorator(f):
f._A_route = r
return f
return decorator
class A (object):
def __init__(self):
self.rmap = {}
def register(self, *objects):
for obj in objects:
self._register(obj)
def _register(self, obj):
for name in dir(obj):
if name.startswith("_"):
continue
attr = getattr(obj, name)
if callable(attr) and hasattr(attr, "_A_route"):
self.rmap[attr._A_route] = attr
def run(self):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](data)
class B (object):
def __init__(self):
pass
#route('/one/way')
def one_way (self, data):
print('A WAY:{}'.format(self))
if __name__ == "__main__":
a = A()
b = B()
a.register(b)
a.run()
Now there might be better solutions for your concrete use case, but it's impossible to tell without knowing about the whole context etc.
When calling self.rmap[k](data) you are not passing in the self parameter. This has to be an instance of class B in order to work.
Normally you'd just pass on the parameters with which the decorated function was called, but you seem to want to use your decorated function differently. In your case what would work is:
def run(self):
data = [1,2,3]
b = B()
for k in self.rmap.keys():
self.rmap[k](b, data)
You could of course also instantiate the B instance somewhere else if you want to reuse it between calls.
I have a question related to OOP but it should be implemented in Python.
I have a file user_inputs.py with all the user parameters.
In my main file, I have a function that is called first. This function is responsible to read all the user parameters and return a dictionary that will be used in the rest of the program.
My question is: what is the cleanest way to pass the user_parameters dictionary to all classes? I did it in 2 ways:
Method 1)
def read_user_parameters():
# code to open and read all parameters etc.
return user_parameters # returns a dictionary with all the user parameters
Class A():
def __init__(self, user_parameters):
self.user_parameters = user_parameters
Class B():
def __init__(self, user_parameters):
self.user_parameters = user_parameters
user_parameters = read_user_parameters()
object_A = A(user_parameters)
object_B = B(user_parameters)
I don't like this way because I have dozens of classes that need to pass this argument. So I thought to create a parent class with the user parameters:
Method 2)
Class User_parameters():
def __init__(self, user_parameters):
def read_user_parameters():
# code to open and read all parameters etc.
return user_parameters
Class A(User_parameters):
__init__(self, user_parameters):
super().__init__()
# self.user_parameters comes from the parent class now
Class B(User_parameters):
__init__(self, user_parameters):
super().__init__()
# self.user_parameters comes from the parent class now
object_A = A()
object_B = B()
I prefer method 2, however, when super() is initialized from Class A and Class B the function read_user_parameters() that reads the file will be called twice (multiply this by dozens of times). Is there a better solution than method 1 in which I call read_user_parameters() only once but doesn't need to pass the argument for all classes?
Thank you for your time.
Why not just have a single UserParameters class and two objects of the same class (Also class nameds are supposed to be camel-cases, not snake-cased)
#Single class for user parameters
class UserParameters:
def __init__(self, user_parameters):
self.user_parameters = user_parameters
def read_user_parameters(self):
# code to open and read all parameters etc.
return self.user_parameters
#Two objects
object_A = UserParameters("<params>")
object_B = UserParameters("<params>")
for a specific framework i work with, i need to define object attributes as special classes, for example, instead of writing this:
class A:
def __init__(self):
self.some_int = 2
i would need to write:
class A:
def __init__(self):
self.some_int = SpecialIntWrapper(name = "some_int", value = 2)
I would like to somehow override operators/methods so that typing the first code (self.some_int = 2) will call SpecialIntWrapper behind the scenes, with the attribute name and value.
is this possible?
Basically there are two ways - via a #property decorator (preferable unless you want to affect arbitrary names)
class MyClass:
def __init__(self):
self.some_int = 2
# if you know the name of the property define it as a property - a getter
#property
def some_int(self):
return self._some_int
# and a setter
#some_int.setter
def some_int(self, value):
self._some_int = SpecialIntWrapper("some_int", value)
or overloading the __setattr__ magic method
class MyClass:
def __init__(self):
self.some_int = 2
def __setattr__(self, name, value):
# in general if you dont know the names of the properties
# beforehand you can somehow filter them here
if name == "some_int":
super().__setattr__(name, SpecialIntWrapper(name=name, value=value))
else:
# to use the setattr in a default way, just call it via super(Python 3)
super().__setattr__(name, value)
either way the some_int will be initialized to the SpecialIntWrapper instance
>>>print(MyClass().some_int)
<__main__.SpecialIntWrapper object at 0x03721810>
Something like this
class SpecialIntWrapper:
def __init__(self, name, value):
pass
class MyClass:
def __init__(self):
self.some_int = 3
def __setattr__(self, key, value):
if key == 'some_int':
self.__dict__[key] = SpecialIntWrapper(key, value)
else:
self.__dict__[key] = value
print(MyClass().some_int)
# >>> <__main__.SpecialIntWrapper object at 0x1076f1748>