Using decorator in class method and populate variable - python

I'm writing some code and I have a dictionary where the key is any string, and the value is a function. I then loop through each key in the dictionary and call the functions, like so:
class SomeClass:
dictionary = {}
# Not sure how to use this decorator function
def decorator(key):
def wrapper(funct):
self.dictionary[key] = funct
return funct
return wrapper
#decorator("some_val1")
def function_1(self):
...
#decorator("some_val2")
def function_2(self):
...
#decorator("some_val3")
def function_3(self):
...
def execute_all_functions(self):
for key, _ in self.dictionary.items():
self.dictionary[key]()
if __name__ == "__main__":
sclass = SomeClass()
sclass.execute_all_functions()
So this should populate dictionary with:
{
"some_val1": function_1(),
"some_val2": function_2(),
"some_val3": function_3()
}
I'm getting this error though
self.dictionary[key] = funct
NameError: name 'self' is not defined
How would I be able to do this. Help appreciated.

I don't think it's possible.
First you should read this: https://docs.python.org/3.3/howto/descriptor.html , to know the difference of function vs method.
In your code, the key equals self of a method.
def decorator(key):
def wrapper(funct):
self.dictionary[key] = funct
return funct
return wrapper
If you want to use a class's property, you should refer by cls. Correct code might be:
#classmethod
def decorator(cls, key):
def wrapper(funct):
self.dictionary[key] = funct
return funct
return wrapper
So let's say if you want to update a class property, you must have cls reference. I have tried the code below, make the decorator_maker a classmethod.
class SomeClass:
dictionary = {}
#classmethod
def decorator_maker(cls, key):
print(cls, key)
def decorator(funct):
cls.dictionary[key] = funct
return funct
return decorator
#decorator_maker("some_val1")
def function_1(self):
...
#decorator_maker("some_val2")
def function_2(self):
...
#decorator_maker("some_val3")
def function_3(self):
...
def execute_all_functions(self):
for key, _ in self.dictionary.items():
self.dictionary[key]()
And you will get an error like TypeError: 'classmethod' object is not callable. Same as this question: 'classmethod' object is not callable. AKA, you can't call a classmethod until the class is defined.
So you may want to make the decorator outside the class. But for the same reason, you can't get a reference of cls until the classmethod. method is also an attribute of class, you can't dynamic change an attribute while define another. see Python class method run when another method is invoked.
It would be easier if you move dictionary outside the class.

functools.wraps might be useful. In the bellow trivial example, without wraps the decorator will not function correctly.
from functools import wraps
class SomeClass:
var = 1
#wraps
def decorator(self, fn):
return fn
#decorator
def return_value(self):
print(self.var)
return self.var
if __name__ == "__main__":
sclass = SomeClass()
sclass.return_value()

Related

Python - Class object as function argument: Object only - Class not in argument

I am trying to write a function taking a string as an argument and using this argument as a class object.
Note that my explanantion might be strangely formulated sice I could not find an answer online. The MWE below should clarify what I mean, the problematic line is indicated.
Edit: in the MWE, "print" is an example. I need to be able to call the object to update it, print it or, in the case of a list, append to it. I need access to the object itself, not the value of the object.
MWE
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Define function
def myFunction (parameter):
print(myObject.parameter)##### This line is currently not possible.
# Use class
myObject = myClass()
# Use function
myFunction("one")
I am not trying to append a new object to the class, only to call an existing object.
Is this even possible?
Looks like you need the built-in function called getattr
my_object = myClass()
def my_function(parameter):
print(getattr(my_object, parameter, None))
also this is not the best practice to call objects from outer scope like that. i'd suggest to use dict magic methods:
class MyClass:
def __init__(self):
self.one = "Test"
self.two = "Plop"
def __getitem__(self, parameter):
return getattr(self, parameter, None)
def __setitem__(self, parameter, value):
return setattr(self, parameter, value)
my_obj = MyClass()
parameter = "x"
print(my_obj[parameter])
my_obj[parameter] = "test"
print(my_obj.x)
You need to use getarttr():
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Use class
myObject = myClass()
# Define function
def myFunction(parameter):
print(getattr(myObject, parameter))##### This line is currently possible.
# Use function
myFunction("one")

Python, how to alter a method of another class' instance

I would like to add some small functionality to a method of another class' instance.
What I tried is listed below. With the same approach it's possible to completely change the method. But I would like to keep a slightly altered version of the original method. Is that possible?
BTW: The classes can not inherit from one another, because I don't necessarily know what class B is.
class A:
def alter_fn(self, obj):
def wrapper():
func = getattr(obj, obj.fn_name)
out = func()
print('out = ', out)
return out
setattr(obj, obj.fn_name, wrapper)
return
class B:
def __init__(self) -> None:
self.fn_name = 'fn'
def fn(self):
return 123
a=A()
b=B()
a.alter_fn(b)
b.fn()
For obvious reasons this raises a recursion depth error but I do not know how to avoid that. I also tried func = copy(getattr(obj, obj.fn_name))
After altering the method b.fn I would like it to return the value, the unaltered function would return (here 123) but I would also like it to do something else with this output, in this example print it.
Move the assignment of func above the definition of wrapper.
...
def alter_fn(self, obj):
func = getattr(obj, obj.fn_name)
def wrapper():
...
This way getattr is called only once and wrapper can access func as a closure variable.
You just need to take the func = getattr(obj, obj.fn_name) out of the wrapperfunction.
class A:
def alter_fn(self, obj):
func = getattr(obj, obj.fn_name)
def wrapper():
out = func()
print('out = ', out)
return out
setattr(obj, obj.fn_name, wrapper)
return
You need to get the wrapped function before re-assigning it:
def alter_fn(self, obj):
func = getattr(obj, obj.fn_name)
def wrapper():
out = func()
print('out = ', out)
return out
setattr(obj, obj.fn_name, wrapper)
return
This way func is assigned only once, when the original function is called. Thus, you can safely call it without calling wrapper() again by accident.
Also consider using functools.wraps() to keep the docstring and name information of the string the same.

How to add methods of class to a list inside the class with a decorator

I would like to update a "class-wide" list from a decorator that decorates the class' methods and adds each decorated method to that list.
This is what came to mind:
def add(meth: callable):
Spam.eggs.append(func)
return meth
class Spam:
eggs = []
#add
def meth(self):
pass
This won't work though because Spam hasn't finished defining itself when #add is reached, and thus add raises a NameError, as pointed out in the comments.
I also tried a class method:
class Spam:
eggs = []
#classmethod
def add(cls, meth: callable):
cls.eggs.append(meth)
return meth
#add
def meth(self):
pass
But this doesn't work either because when #add is reached, add is bound to the classmethod decorated instance, which is not callable.
Here is what I need this for:
I have a class with several methods that take one argument (besides self) that transform that object in such a way that these methods may be composed with one another. I want to decorate each of these in such a way that they're automatically added to a list in the class.
E.g.:
from typing import List
def transform_meth(meth: callable):
TextProcessor.transforms.add(meth)
return meth
class TextProcessor:
transforms: List[callable] = []
#transform_meth
def m1(self, text):
return text
#transform_meth
def m2(self, text):
return text
def transform(self, text):
for transform in self.transforms:
text = transform(text)
return text
I could add the methods in the list manually, but I find the decorator to be clearer since it is close to the definition of the method, and thus it is easier to remember to decorate a new method when defining it than adding it to the list manually.
Your current approach fails because when transform_meth is called, TextProcessor isn't bound to anything yet (or if it is, that object gets overwritten when the class statement completes).
The simple solution would be to define transform_meth inside the class statement, so that it could simply declare transforms as a nonlocal variable. However, that won't work because a class statement doesn't establish a new scope.
Instead, you can define a function that creates the decorator, which takes the desired list (at that point a just a name in the body of the class statement, not from any assumed scope). That function returns a closure over the list argument
so that you can append to it.
def make_decorator(lst):
# *This* will be the function bound to the name 'transform_meth'
def _(meth):
lst.append(meth)
return meth
return _
class TextProcessor:
transforms: List[callable] = []
transform_meth = make_decorator(transforms)
#transform_meth
def m1(self, text):
return text
#transform_meth
def m2(self, text):
return text
def transform(self, text):
for transform in self.transforms:
text = transform(text)
return text
del transform_meth # Not needed anymore, don't create a class attribute
Since the arg of each method is self you can append to the object instance like so:
from functools import wraps
def appender(f):
#wraps(f)
def func(*args, **kwargs):
if f not in args[0].transforms:
args[0].transforms.append(f)
return f(*args, **kwargs)
return func
class Foo(object):
def __init__(self):
self.transforms = []
#appender
def m1(self, arg1):
return arg1
#appender
def m2(self, arg1):
return arg1
def transform(self, text):
methods = [f for f in dir(self) if not f.startswith("__") and callable(getattr(self,f)) and f != 'transform']
for f in methods:
text = getattr(self,f)(text)
return text
f = Foo()
f.transform('your text here')
print(f.transforms)
Output:
[<function Foo.m1 at 0x1171e4e18>, <function Foo.m2 at 0x1171e4268>]

How to get the method name of a decorated property method?

Lets say I have a method which I decorate with #property.
Is there a way to get the underlying name of the decorated method?
How could I for instance print the name of the property when it does not have a __name__ attribute?
I'm trying to do a RPC call and everything works for methods and static functions but not this.
A property has the following attributes: fget, fset and fdel, which are references to the getter/setter/deleter functions.
So, you can get the __name__ of each of this functions (if not None)
Demo:
class Thing(object):
#property
def foo(self):
return "foo"
def get_baz(self):
return "baz"
bar = property(get_baz)
>>> Thing.foo.fget.__name__
'foo'
>>> Thing.bar.fget.__name__
'get_baz'
i think if use decorator #property you can access your function with name of function and you can give return value.
class Foo:
value = 'Foo'
def setName(self, name):
self.value = name
#property
def name(self):
return "You name is: {}".format(self.value)
foo = Foo()
print(foo.name)
foo.setName('Bar')
print(foo.name)
If you use functools.wraps() in your decorator, the decorated function will have the __name__ of the wrapped function.
IF you had some sample code for the decorator in your question, I could show more precisely how to do this...

How can I access a classmethod from inside a class in Python

I would like to create a class in Python that manages above all static members. These members should be initiliazed during definition of the class already. Due to the fact that there will be the requirement to reinitialize the static members later on I would put this code into a classmethod.
My question: How can I call this classmethod from inside the class?
class Test():
# static member
x = None
# HERE I WOULD LOVE TO CALL SOMEHOW static_init!
# initialize static member in classmethod, so that it can be
#reinitialized later on again
#classmethod
def static_init(cls):
cls.x = 10
Any help is appreciated!
Thanks in advance,
Volker
At the time that x=10 is executed in your example, not only does the class not exist, but the classmethod doesn't exist either.
Execution in Python goes top to bottom. If x=10 is above the classmethod, there is no way you can access the classmethod at that point, because it hasn't been defined yet.
Even if you could run the classmethod, it wouldn't matter, because the class doesn't exist yet, so the classmethod couldn't refer to it. The class is not created until after the entire class block runs, so while you're inside the class block, there's no class.
If you want to factor out some class initialization so you can re-run it later in the way you describe, use a class decorator. The class decorator runs after the class is created, so it can call the classmethod just fine.
>>> def deco(cls):
... cls.initStuff()
... return cls
>>> #deco
... class Foo(object):
... x = 10
...
... #classmethod
... def initStuff(cls):
... cls.x = 88
>>> Foo.x
88
>>> Foo.x = 10
>>> Foo.x
10
>>> Foo.initStuff() # reinitialize
>>> Foo.x
88
You call a class method by appending the class name likewise:
class.method
In your code something like this should suffice:
Test.static_init()
You could also do this:
static_init(Test)
To call it inside your class, have your code do this:
Test.static_init()
My working code:
class Test(object):
#classmethod
def static_method(cls):
print("Hello")
def another_method(self):
Test.static_method()
and Test().another_method() returns Hello
You can't call a classmethod in the class definition because the class hasn't been fully defined yet, so there's nothing to pass the method as its first cls argument...a classic chicken-and-egg problem. However you can work around this limitation by overloading the __new__() method in a metaclass, and calling the classmethod from there after the class has been created as illustrated below:
class Test(object):
# nested metaclass definition
class __metaclass__(type):
def __new__(mcl, classname, bases, classdict):
cls = type.__new__(mcl, classname, bases, classdict) # creates class
cls.static_init() # call the classmethod
return cls
x = None
#classmethod
def static_init(cls): # called by metaclass when class is defined
print("Hello")
cls.x = 10
print Test.x
Output:
Hello
10
After re-reading your question carefully this time I can think of two solutions. The first one is to apply the Borg design pattern. The second one is to discard the class method and use a module level function instead. This appears to solve your problem:
def _test_static_init(value):
return value, value * 2
class Test:
x, y = _test_static_init(20)
if __name__ == "__main__":
print Test.x, Test.y
Old, incorrect answer:
Here's an example, I hope it helps:
class Test:
x = None
#classmethod
def set_x_class(cls, value):
Test.x = value
def set_x_self(self):
self.__class__.set_x_class(10)
if __name__ == "__main__":
obj = Test()
print Test.x
obj.set_x_self()
print Test.x
obj.__class__.set_x_class(15)
print Test.x
Anyway, NlightNFotis's answer is a better one: use the class name when accessing the class methods. It makes your code less obscure.
This seems like a reasonable solution:
from __future__ import annotations
from typing import ClassVar, Dict
import abc
import string
class Cipher(abc.ABC):
#abc.abstractmethod
def encrypt(self, plaintext: str) -> str:
pass
#abc.abstractmethod
def decrypt(self, ciphertext: str) -> str:
pass
class RotateCipher(Cipher, abc.ABC):
#staticmethod
def rotate(n: int) -> str:
return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]
class VigenereCipher(RotateCipher):
_TABLE: ClassVar[Dict[str, str]] = dict({(chr(i + ord("A")), RotateCipher.rotate(i)) for i in range(26)})
def encrypt(self, plaintext: str) -> str:
pass
def decrypt(self, plaintext: str) -> str:
pass
vc = VigenereCipher()
The method is now a static method of the cipher, nothing outside the classes is referenced. You could opt to name RotateCipher _RotateCipher instead, if you don't want people using it by itself.
Note: I removed the Final, as I ran this on 3.7, but after reading the documentation on Final, I don't think it would affect the solution? Also added an import for string which the question was missing. And finally added an implementation for the abstract methods, alternatively, could have let VigenereCipher inherit from abc.ABC as well.
If your classmethod is not used very often do a lazy evaluation
class A() {
# this does not work: x=A.initMe()
#classmethod
def initMe(cls) {
if not hasattr(cls,"x"):
# your code her
cls.x=# your result
pass
#classmethod
def f1(cls) {
# needs initMe
cls.initMe()
# more code using cls.x
}
}

Categories

Resources