Execute class function stored in variable on demand - python

I have two classes with functions:
from functools import partial
class A:
def __init__(self, collection):
self.collection = collection
def filter(self, val):
for element in self.collection:
if element.var == val:
return element
class B:
def __init__(self, var):
self.var = var
def test(self):
print('Element with variable ', self.var)
Now I want to have a class that can call a function on an object, fetched on the fly by another function, both stored in a variable and all executed when a certain function was called:
class C:
def __init__(self, fetch, function):
self.fetch = fetch
self.function = function
def run(self):
global base
# -----
# This is the code I need
base.fetch().function()
# ... and currently it's completely wrong
# -----
c = C(partial(A.filter, 5), B.test)
base = A([B(3), B(5), B(8)])
c.run()
Should print: Element with variable 5

You should pass base into run instead of messing with global. base doesn't have a fetch method, so you have to call the fetch function you have as an attribute with base as an argument. You can then send the return value of that call to function.
You're also applying partial to A.filter slightly wrong. Positional arguments are applied in order, so partial(A.filter, 5) will try to bind 5 to self, which will throw everything off. Instead, we need to give it the name of the parameter we wish to bind 5 to.
class C:
def __init__(self, fetch, function):
self.fetch = fetch
self.function = function
def run(self, a):
return self.function(self.fetch(a))
c = C(partial(A.filter, val=5), B.test)
c.run(A([B(3), B(5), B(8)]))
# Element with variable 5

Related

Add a function to a dictionary using a decorator

i want to add functions to a dictionary that is stored in a object using decorarotors.
I made a class and added a function called 'add'. The function takes a key and a function.
I want it that when i call the 'add' function, the function that i define underneath will be added to my dictionary with the given key in the decorator.
I got it working with just adding it to list, but i want to access the functions specificly with a key.
Here's my code:
class App:
def __init__(self):
self.functions = {}
def add(self, key, func):
self.functions[key] = func
app = App()
#app.add("hello")
def print_hello():
print("hello")
Here's the Error:
#app.function("hello")
TypeError: function() missing 1 required positional argument: 'func'
And here the working code with a list:
class App:
def __init__(self):
self.functions = []
def add(self, func):
self.functions.append(func)
def loop_functions(self):
for f in self.functions:
f()
app = App()
#app.add
def print_hello():
print("hello")
app.loop_functions()
You don't really need two arguments if you are okay with having the key as the actual function name, then you can use the .__name__ to get the name of the function which will be the key in your self.functions dictionary and the value will be the function itself.
You can use the following:
class App:
def __init__(self):
self.functions = {}
def add(self, func):
self.functions[func.__name__] = func
app = App()
#app.add
def bye():
print('Goodbye')
>>> app.functions
# {'bye': <function __main__.bye()>}
>>> app.functions['bye']()
# Goodbye
Found an answer:
I needed to add another decorator inside:
def add(self, key):
def adder(func):
self.functions[key] = func
return func
return adder
But as another user said:
I could've access the function name with '_name_' and use it as a key.

How to detect changes of a list whithin a Python class, or why does "setter" not trigger

I would like to have a list in a Python class. Whenever an element in the list is changed I need to run some logic. I'm pretty new to classes in python and my approach with the setter might be pretty naive. This is what makes intuitive sense to me:
class test():
def __init__(self):
self._R = [False]*16
#property
def R(self):
return self._R
#R.setter
def R(self,a):
print('Why do I not get reached?')
self._R = a
W = test()
W.R[0] = True
But the setter never gets triggered. If you could give me a notch in the right direction, I would be very great full.
You can create a new List-like class that takes a callback function and executes it whenever the list is changed:
class CallbackList: # PEP-8 style suggests UpperCase class names
def __init__(self, callback=None):
self._list = [False]
self._callback = callback # Python functions are first-class objects just like ints, strings, etc, so this is completely legal
def __setitem__(self, index, value):
self._list[index] = value
if self._callback:
self._callback() # Executes the callback function whenever a value is set
def __getitem__(self, index):
return self._list[index]
class Test:
def __init__(self):
self.callback_list = CallbackList(callback=self.foo)
def foo(self):
print("You changed the list!")
W = Test()
W.callback_list[0] = True # This prints "You changed the list!"
Note that this still won't catch every possible change. For example:
W = Test()
some_list = [1, 2, 3]
W.callback_list[0] = some_list # This triggers the callback function
print(W.callback_list[0]) # [1, 2, 3]
some_list.append(4) # This does NOT trigger the callback function!
print(W.callback_list[0]) # [1, 2, 3, 4] !
I tried to write code according to #user2357112supportsMonica comments:
class test():
def __init__(self):
self.A = 1
def foo(self):
print(self.A)
class R_Class():
def __init__(self):
self._R = [False]*16
def __setitem__(self,index,value):
self._R[index] = value
test.foo() #Here I need to call foo somehow
def __getitem__(self,index):
return self._R[index]
R = R_Class()
W = test()
W.R[0] = True
But this approach leads to another problem, is there a way to properly call the foo function from within the sub class?

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>]

Can't call static method inside class

I am trying to call a static method inside a class to populate the class variable.
import sys
import os
from HelpingData import *
class Inventory(object):
shipping_cost = 400.0
total_stock = calculate_total_stock.__func__()
def __init__(self, attributes={}):
self.inventory = {}
if attributes is None:
self.inventory = {}
else:
for key in attributes:
self.inventory[key] = attributes[key]
def getValue(self,attribute):
return self.inventory[attribute]
def setValue(self,attribute,value):
self.inventory[attribute]=value
#staticmethod
def calculate_total_stock():
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
And this is the error I am getting:
total_stock = calculate_total_stock.__func__()
NameError: name'calculate_total_stock' is not defined
What am I missing here?
You really don't need any workaround here, just give the calling method an additional level of direction.
In the example below you can call the PrintThis() method both internal and external to its defining class.
External:
Call as you normally would
MyClass.PrintThis('42')
Internal:
You must add self or the containing class
MyClass.PrintThis('42')
self.PrintThis('42')
To produce the error:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
The Fix:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
self.PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
Run It
class Run:
def __init__(self):
mc = MyClass()
MyClass.PrintThis('From Outside')
mc.IncrementValue()
mc.IncrementValue()
My Value: From Outside
My Value: From MyClass 1
My Value: From MyClass 2
Why?
I'm not sure :-)
The only thing I noticed is that the static method (PrintThis) is a function, while the non-static method is a bound method.
I am sure there is some explanation to this behavior in Pythons documentation. Please share if you look it up :-)
I know this question is a few years old at this point, however it was the first hit when I googled the fault.
The code at the top level of the Inventory definition (i.e. class attributes and method definitions) runs before the name Inventory exists, so you can't call its own methods within the definition. As you have a #staticmethod, which doesn't require any class or instance argument, why not move it outside?
def calculate_total_stock(product_names, product_stock):
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
class Inventory(object):
SHIPPING_COST = 400.0
TOTAL_STOCK = calculate_total_stock(product_names, product_stock)
def __init__(self, attributes=None):
self.inventory = {}
if attributes is not None:
for key in attributes:
self.inventory[key] = attributes[key]
def get_value(self, attribute):
return self.inventory[attribute]
def set_value(self, attribute, value):
self.inventory[attribute] = value
Note that I have done some tidying up, particularly in terms of style and making the explicit arguments to calculate_total_stock.

Wrap calls to methods of a Python class

I would like to wrap a number of class methods in Python with the same wrapper.
Conceptually it would look something like this in the simplest scenario:
x = 0 # some arbitrary context
class Base(object):
def a(self):
print "a x: %s" % x
def b(self):
print "b x: %s" % x
class MixinWithX(Base):
"""Wrap"""
def a(self):
global x
x = 1
super(MixinWithX, self).a()
x = 0
def b(self):
global x
x = 1
super(MixinWithX, self).a()
x = 0
Of course, when there are more methods than a and b, this becomes a mess. It seems like there ought to be something simpler. Obviously x could be modified in a decorator but one still ends up having a long list of garbage, which instead of the above looks like:
from functools import wraps
def withx(f):
#wraps(f) # good practice
def wrapped(*args, **kwargs):
global x
x = 1
f(*args, **kwargs)
x = 0
return wrapped
class MixinWithX(Base):
"""Wrap"""
#withx
def a(self):
super(MixinWithX, self).a()
#withx
def b(self):
super(MixinWithX, self).b()
I thought about using __getattr__ in the mixin, but of course since methods such as a and b are already defined this is never called.
I also thought about using __getattribute__ but it returns the attribute, not wrapping the call. I suppose __getattribute__ could return a closure (example below) but I am not sure how sound a design that is. Here is an example:
class MixinWithX(Base):
# a list of the methods of our parent class (Base) that are wrapped
wrapped = ['a', 'b']
# application of the wrapper around the methods specified
def __getattribute__(self, name):
original = object.__getattribute__(self, name)
if name in wrapped:
def wrapped(self, *args, **kwargs):
global x
x = 1 # in this example, a context manager would be handy.
ret = original(*args, **kwargs)
x = 0
return ret
return wrapped
return original
It has occurred to me that there may be something built into Python that may alleviate the need to manually reproduce every method of the parent class that is to be wrapped. Or maybe a closure in __getattribute__ is the proper way to do this. I would be grateful for thoughts.
Here's my attempt, which allows for a more terse syntax...
x = 0 # some arbitrary context
# Define a simple function to return a wrapped class
def wrap_class(base, towrap):
class ClassWrapper(base):
def __getattribute__(self, name):
original = base.__getattribute__(self, name)
if name in towrap:
def func_wrapper(*args, **kwargs):
global x
x = 1
try:
return original(*args, **kwargs)
finally:
x = 0
return func_wrapper
return original
return ClassWrapper
# Our existing base class
class Base(object):
def a(self):
print "a x: %s" % x
def b(self):
print "b x: %s" % x
# Create a wrapped class in one line, without needing to define a new class
# for each class you want to wrap.
Wrapped = wrap_class(Base, ('a',))
# Now use it
m = Wrapped()
m.a()
m.b()
# ...or do it in one line...
m = wrap_class(Base, ('a',))()
...which outputs...
a x: 1
b x: 0
You can do this using decorators and inspect:
from functools import wraps
import inspect
def withx(f):
#wraps(f)
def wrapped(*args, **kwargs):
print "decorator"
x = 1
f(*args, **kwargs)
x = 0
return wrapped
class MyDecoratingBaseClass(object):
def __init__(self, *args, **kwargs):
for member in inspect.getmembers(self, predicate=inspect.ismethod):
if member[0] in self.wrapped_methods:
setattr(self, member[0], withx(member[1]))
class MyDecoratedSubClass(MyDecoratingBaseClass):
wrapped_methods = ['a', 'b']
def a(self):
print 'a'
def b(self):
print 'b'
def c(self):
print 'c'
if __name__ == '__main__':
my_instance = MyDecoratedSubClass()
my_instance.a()
my_instance.b()
my_instance.c()
Output:
decorator
a
decorator
b
c
There are two general directions I can think of which are useful in your case.
One is using a class decorator. Write a function which takes a class, and returns a class with the same set of methods, decorated (either by creating a new class by calling type(...), or by changing the input class in place).
EDIT: (the actual wrapping/inspecting code I had in mind is similar to
what #girasquid has in his answer, but connecting is done using decoration instead of mixin/inheritance, which I think is more flexible an robust.)
Which brings me to the second option, which is to use a metaclass, which may be cleaner (yet trickier if you're not used to working with metaclasses). If you don't have access to the definition of the original class, or don't want to change the original definition, you can subclass the original class, setting the metaclass on the derived.
There is a solution, and it's called a decorator. Google "python decorators" for lots of information.
The basic concept is that a decorator is a function which takes a function as a parameter, and returns a function:
def decorate_with_x(f)
def inner(self):
self.x = 1 #you must always use self to refer to member variables, even if you're not decorating
f(self)
self.x = 0
return inner
class Foo(object):
#decorate_with_x # #-syntax passes the function defined on next line
# to the function named s.t. it is equivalent to
# foo_func = decorate_with_x(foo_func)
def foo_func(self):
pass

Categories

Resources