Calling function from defined class in python? - python

I have this code:
import numpy as np
class Person:
...
class Bob:
def __init__(self, age, gender="Male"):
self.age = age
self.gender = gender
def walk(x):
return x / (180 * np.exp(-self.age / 35))
bob_1 = Person().Bob(37)
I want it to call the walk(x) function when calling bob_1(x) but still be able to call other methods as bob_1.method(args).
Would Python happen to provide a way to do so?

Defining a class within a class makes the inner class count as a class-level variable, not an instance variable.
Doing this should work:
bob_1 = Person.Bob(37) # note that we're not calling Person
print(bob_1.walk(30))
Note that .walk() is currently defined as a class method - to make it an instance method, you should define it as
def walk(self, x):
The self is passed implicitly when you call the method on an instance like bob_1.
If you want to have an instance display some behavior when you call it, then you can define the __call__() method on the class:
>>> class A:
... def __call__(self, *args):
... print(args)
...
>>> g = A()
>>> g(3, 4, 5)
(3, 4, 5)
In your case, in order to make bob_1(30) behave like bob_1.walk(30), you would do
def __call__(self, x):
return self.walk(x)

Related

Is there a way to pass a function call to an inner object?

Is there a way in python to pass a function call to an inner object, maybe through a decorator or wrapper? In the example below, class A holds a list of class B objects, and one of the class B objects is selected as the active object. I want class A to function as a passthrough, just identifying which of the class B objects that the call goes to. However, class A doesn't know what type of class it is going to hold beforehand, so I can't just add a set_var function to class A. It has to work for any generic function that class B has. It will only have one type of class in its objects list, so it could take class B as an input when it is instantiated and dynamically create functions, if that's a possibility. The client wouldn't know whether it's dealing with class A or class B. The code below is as far as I got.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, object):
self.objects.append(object)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
You could use the generic __getattr__ to delegate to the wrapped object.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, obj):
self.objects.append(obj)
self.current_object = obj
def __getattr__(self, name):
return getattr(self.current_object, name)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
print(b_obj2.var)
That will print "100".
You will still get an AttributeError if the wrapped object doesn't have the expected method.
It was interesting to look at this, it is intentionally rough but it does indeed allow you to call one the B instance's set_var methods.
The code below uses sets as a quick and dirty way to see the difference in callable methods, and if there is; it sets the attribute based on that name. Binding the method to the A instance.
This would only bind set_var once from the first object given.
def add_object(self, object):
self.objects.append(object)
B_methods = set([m for m in dir(object) if callable(getattr(object, m))])
A_methods = set([m for m in dir(self) if callable(getattr(self, m))])
to_set = B_methods.difference(A_methods)
for method in to_set:
setattr(self, method, getattr(object, method))

Apply a function to all instances of a class

I am looking for a way to apply a function to all instances of a class. An example:
class my_class:
def __init__(self, number):
self.my_value = number
self.double = number * 2
#staticmethod
def crunch_all():
# pseudocode starts here
for instances in my_class:
instance.new_value = instance.my_value + 1
So the command my_class.crunch_all() should add a new attribute new_value to all existing instances. I am guessing I will have to use #staticmethod to make it a "global" function.
I know I could keep track of the instances that are being defined by adding something like my_class.instances.append(number) in __init__ and then loop through my_class.instances, but I had no luck so far with that either. Also I am wondering if something more generic exists. Is this even possible?
Register objects with the class at initialisation (i.e. __init__) and define a class method (i.e. #classmethod) for the class:
class Foo(object):
objs = [] # registrar
def __init__(self, num):
# register the new object with the class
Foo.objs.append(self)
self.my_value = num
#classmethod
def crunch_all(cls):
for obj in cls.objs:
obj.new_value = obj.my_value + 1
example:
>>> a, b = Foo(5), Foo(7)
>>> Foo.crunch_all()
>>> a.new_value
6
>>> b.new_value
8

Class method as a decorator

I have a class where I have multiple methods. I want to use one of the methods as a decorator for other methods. For this I am using following syntax:
#self.action
def execute(self,req):
where action is other method in my class. But it doesn't work and throws exception as
name 'self' is not defined
You cannot use a method of the class while defining it; there is no self within the class nor is the class 'baked' yet to even access any class.
You can treat methods as functions to use as a decorator:
class SomeClass():
def action(func):
# decorate
return wrapper
#action
def execute(self, req):
# something
If action is defined on a base class, then you'd have to refer to the name via the base class:
class Base():
#staticmethod
def action(func):
# decorate
return wrapper
class Derived(Base):
#Base.action
def execute(self, req):
# something
For Python 2, you'd have to make action a static method here, as otherwise you get an unbound method that'll complain you cannot call it without an instance as the first argument. In Python 3, you can leave off the #staticmethod decorator there, at least for the purposes of the decorator.
But note that action cannot then be used as a method directly; perhaps it should not be part of the class at all at that point. It is not part of the end-user API here, presumably the decorator is not used by consumers of the instances of these classes.
Just beware that both the decorator and the decorated function are unbound methods, so you can only access the self (or cls for classmethods) in the inner scope of the decorator, and must manually bind the decorated method to the instance bound in the inner decorator.
class A:
x = 5
y = 6
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate
def func(self):
return self.y
A().func() # 30!!
Still trying to wrap my head around how decorators could be inherited and overridden.
Beware that for the decorator to work it can't be bound to an instance. That is: there is no way to make this work
a = A()
#a.decorate
def func(*args):
return 1
Despite this pattern is much more common than the asked here.
At this point the question raises: is it a method at all or just code that you happen to hide in a class?
The only way to prevent the decorator being wrongfully bound is to declare it as a staticmethod, but then it must be in a previous super class because to be used it must be bound to the static class reference which would not be yet defined, just as the self.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
class B(A):
#A.decorate
def func(self):
return 1
class C():
x = 2
#B.decorate
def func(self):
return 1
a = A()
class D():
x = 3
#a.decorate
def func(self):
return 1
B().func() # 1
C().func() # 2
D().func() # 3
But as you can see, there is no way for the decorator to use the state of its own class. class A from this last example just happens to be a mixin with a default x variable and an "unrelated" static decorator.
So, again, is it a method?
To overcome all of this, you can bind the staticmethod in your same class to an arbitrary type. Namely, the builtin type will do.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate.__get__(type)
def func(self):
return 1
class B:
x = 2
#A.decorate
def func(self):
return 1
class C:
x = 3
#(A().decorate) # Only for Python 3.9+, see PEP-614
def func(self):
return 1
A().func() # 1
B().func() # 2
C().func() # 3
But this features too much magic for my taste. And still not a method for my gut.
In python "self" is passed to instance methods as an argument (the first), "self" is just a convention is possible to call it "foobarbaz" (of course it would be silly)… the point is that, from the outside "self" is not defined (because its scope is the method)… you can't decorate class methods with other class methods, instead you have to write a separate class!

Python: Dynamically adding function to a class, whose name is contained in a string

What is the best way to create a new member function of a class with function name contained as string? Also, this new function is merely as pass-through for another object(helper class), which has the same function name but with variable arguments. I use lambda to achieve this, but I don't know how to handle the scenario, where my pass-through wrapper would be more than one-statement (which is my requirement)
# This is a helper class
class Compensation:
def bonus(self):
return 10000
def salary(self):
# Do something
def stack(self):
# Do something
# This is a employer class
class employee:
def __init__(self):
self.compensation = Compensation()
# This is a wrapper that creates the function
def passThru(funcName):
fn = "employee."+funcName+"=" + "lambda self, *arg: self.compensation." + funcName +"(*arg)"
exec(fn)
fnNames = ["bonus", "salary", "stocks"]
for items in fnNames: passThru(items)
emp = employee()
emp.bonus() # returns 1000
All that trickery with exec gives me a headache ;-) I'm not exactly clear on what you want to do, but adding a new method with a name given by a string is really quite easy. For example,
class employee:
pass
# Some multiline-function.
def whatever(self, a, b):
c = a + b
return c
e = employee()
# Make `whatever` an `employee` method with name "add".
setattr(employee, "add", whatever)
print e.add(2, 9)
Whenever you're reaching for exec, you're probably missing a straightforward way.
EDIT: an oddity here is that if someone tries to display e.add, they'll get a string claiming its name is whatever. If that bothers you, you can add, e.g.,
whatever.__name__ = "add"
Fleshing it out
Is this closer to what you want? Note that #gnibbler suggested much the same, although more telegraphically:
class Compensation:
def bonus(self, a):
return 10000 + a
def salary(self):
return 20000
def stack(self, a=2, b=3):
return a+b
class employee:
def __init__(self):
self.comp = Compensation()
e = employee()
for name in "bonus", "salary", "stack":
def outer(name):
def f(self, *args, **kw):
return getattr(self.comp, name)(*args, **kw)
f.__name__ = name
return f
setattr(employee, name, outer(name))
print e.bonus(9)
print e.salary()
print e.stack(b="def", a="abc")
That displays:
10009
20000
abcdef
All that said, you might want to re-think your architecture. It's strained.
You want setattr. Let's say you have:
>>> inst = Foo(10)
>>> class Foo(object):
def __init__(self, x):
self.x = x
>>> inst = Foo(10)
>>> inst2 = Foo(50)
If you want to add a method to all instances of the class, then setattr on the class. This function will end up being an unbound method on the class, becoming bound in each instance, so it will take the self param:
>>> setattr(inst.__class__, "twice_x", lambda self: self.x * 2)
>>> inst.twice_x()
20
>>> inst2.twice_x()
100
If you want to add the function to just one instance of the class, then setattr on the instance itself. This will be a regular function which will not take the implicit self argument:
>>> setattr(inst, "thrice_x", lambda: inst.x * 3)
>>> inst.thrice_x()
30
>>> inst2.thrice_x()
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
inst2.thrice_x()
AttributeError: 'Foo' object has no attribute 'thrice_x'
You are looking for setattr/getattr.
for func_name in fnNames:
setattr(employee, func_name, (lambda self, *args:getattr(self.compensation, func_name)(*args)))
This still has a problem because you need the lambda function to be closed over func_name. While you could create a closure with another lambda, I'll pull it out into another function for readability
for func_name in fnNames:
def f(func_name): # close the lambda over "func_name"
return lambda self, *args:getattr(self.compensation, func_name)(*args)
setattr(employee, items, f(func_name))

python class variable not visible in __init__?

This code produces an error message, which I found surprising:
class Foo(object):
custom = 1
def __init__(self, custom=Foo.custom):
self._custom = custom
x = Foo()
Can anyone provide enlightenment?
It's Foo that isn't visible, because you're in the middle of building it. But since you're in the same scope as custom, you can just say custom rather than Foo.custom:
class Foo(object):
custom = 1
def __init__(self, mycustom=custom):
self._custom = mycustom
But note that changing Foo.custom later on won't affect the value of custom that subsequently-created Foos see:
class Foo(object):
custom = 1
def __init__(self, mycustom=custom):
self._custom = mycustom
one = Foo()
Foo.custom = 2
two = Foo()
print (two._custom) # Prints 1
By using a sentinel default value instead, you can get what you want:
class Foo(object):
custom = 1
def __init__(self, mycustom=None):
if mycustom is None:
self._custom = Foo.custom
else:
self._custom = mycustom
one = Foo()
Foo.custom = 2
two = Foo()
print (two._custom) # Prints 2
What we do instead is the following
class Foo( object ):
custom = 1
def __init__( self, arg=None )
self._custom = self.custom if arg is None else arg
This bypasses the confusing issue of whether or not the name Foo has been defined yet.
The class body is executed before the class its self is defined, so default argument values can't reference the class. Just making custom the default (without class qualification) should work.
I get the following error:
Traceback (most recent call last):
Line 1, in <module>
class Foo(object):
Line 3, in Foo
def __init__(self, custom=Foo.custom):
NameError: name 'Foo' is not defined
This is because the name Foo is in the process of being defined as the __init__ function is defined, and is not fully available at that time.
The solution is to avoid using the name Foo in the function definition (I also renamed the custom paramter to acustom to distinguish it from Foo.custom):
class Foo(object):
custom = 1
def __init__(self, acustom=custom):
self._custom = acustom
x = Foo()
print x._custom

Categories