Using getattr to call a function in a separate class - python

I may be trying to do something that is outside of the realm of possibility here, but I figured I would ask first before abandoning hope. So here it goes...
I have 2 classes, A and B. Each class has an arbitrary number of functions. Class B will be instantiated somewhere in Class A and Class A will utilize one of Class B functions via that instantiation. A function in Class B will need to refer to one or more of Class A's functions using it's current instantiation data of Class A.
Class A
#!/usr/bin/python
from classB import classB
class classA(object):
def Apple(self):
print("Inside Apple")
b = classB()
b.Banana()
b.bar()
def foo(self):
print("foo inside apple")
a = classA()
a.Apple()
Class B:
#!/usr/bin/python
import inspect
class classB(object):
def Banana(self):
print("Inside banana")
def bar(self):
print("bar inside banana")
'''
The following lines just show I can get the names of the
calling class and methods.
'''
stack = inspect.stack()
the_class = stack[1][0].f_locals["self"].__class__
the_method = stack[1][0].f_code.co_name
print("Caller Class: {}".format(the_class))
print("Caller Method: {}".format(the_method))
function_name = 'foo'
if hasattr(the_class, function_name):
print("Class {} has method {}".format(the_class,
function_name))
getattr(the_class, function_name)()
I get the following error:
getattr(the_class, function_name)()
TypeError: unbound method foo() must be called with classA instance as first argument (got nothing instead)
Thanks!

As the error suggests, you must build an object of classA (i.e. the_class) before calling getattr on it.
objA = the_class()
But taking a step back, why don't you just pass class A to class B while initializing it?
b = classB(self)
That will allow you to access the exact method of class A that you need.
Else, if method 'foo' in class A is supposed to be a static method, make it so by using #staticmethod decorator.

Related

Python: dynamically asserting the class name of a function to insert into its docstring when calling the help function on it

I have a setup that looks as following:
def docstring_formatter(func):
func.__doc__ = func.__doc__.format(class_name=func.__self__.__class__.__name__) #this does not work
return func
class A:
#docstring_formatter
def my_function(self):
"""I am a function of class {class_name}"""
print("test")
class B(A):
pass
docstring = getattr(B.my_function, "__doc__")
>>> AttributeError: 'function' object has no attribute '__self__'
I would like to access the actual class name that the instance of my_function belongs to. Since I am not instantiating the class I when I am using the help() function, the __self__ property is not instantiated yet and I can also not make use of the functools.wraps function. I would like to find a way to also extract the string B or B.my_function when being passed a my_function object that could either belong to A() or B().
Given the code:
class A:
def my_function(self):
print("test")
class B(A):
pass
b = B()
The my_function object has a self binding to the object you took it from, which is a B, so you can do:
>>> b.my_function.__name__
'my_function'
>>> b.my_function.__self__.__class__.__name__
'B'

How can I use a child class' function attribute in the base class?

Overview
I have a python class inheritance structure in which most methods are defined in the base class and most attributes on which those methods rely are defined in child classes.
The base class looks roughly like this:
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractproperty
def property2(self):
pass
def method1(self):
print(self.property1)
def method2(self, val):
return self.property2(val)
while the child class looks like this:
class Child(Base):
property1 = 'text'
property2 = function
where function is a function that looks like this:
def function(val):
return val + 1
Obviously the code above is missing details, but the structure mirrors that of my real code.
The Problem
When I attempt to use method1 in the base class everything works as expected:
>>> child = Child()
>>> child.method1()
'text'
However, attempting the same for method2 spits an error:
>>> child = Child()
>>> child.method2(1) # expected 2
TypeError: method2() takes exactly 1 argument (2 given)
The second passed argument is the Child class itself.
I'm wondering if there's a way to avoid passing this second Child parameter when calling method2.
Attempts
One workaround I've found is to define an abstract method in the base class then build that function in the child classes like so:
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractmethod
def method2(self, val):
pass
def method1(self):
print(self.property1)
class Child(Base):
property1 = 'text'
def method2(self, val):
return function(val)
However, I would prefer that this method live in the base class. Any thoughts? Thanks in advance!
Methods implicitly receive self as the first argument, even if it seems that it is not passed. For example:
class C:
def f(self, x):
print(x)
C.f takes two arguments, but you'd normally call it with just one:
c = C()
c.f(1)
The way it is done is that when you access c.f a "bound" method is created which implicitly takes c as the first argument.
The same happens if you assign an external function to a class and use it as a method, as you did.
Solution 1
The usual way to implement a method in a child class is to do it explicitly there, rather than in an external function, so rather than what you did, I would do:
class Child(Base):
property1 = 'text'
# instead of: property2 = function
def property2(self, val):
return val + 1
Solution 2
If you really want to have property2 = function in the class (can't see why) and function out of the class, then you have to take care of self:
class Child(Base):
property1 = 'text'
property2 = function
def function(self, val):
return val + 1
Solution 3
If you want the previous solution, but without self in function:
class Child(Base):
property1 = 'text'
def property2(self, val):
return function(val)
def function(val):
return val + 1
Solution
Make your method static:
class Child(Base)
property2 = staticmethod(function)
Explanation
As zvone already explained, bound methods implicitly receive self as the first parameter.
To create a bound method you don't necessarily need to define it in the class body.
This:
def foo(self):
print("foo")
class Foo:
bar = foo
f = Foo()
print(f.bar)
will output:
>>> <bound method foo of <__main__.Foo object at 0x014EC790>>
A function assigned to a class attribute will therefore behave just as a normal class method, meaning that if you call it as f.bar() it is treated as a bound method and self is implicitly passed as first parameter.
To control what is and what is not implicitly passed to a class method as first argument is normally controlled with the decorators
#classmethod: the class itself is passed as the first argument
#staticmethod: no arguments are implicitly passed to the method
So you want the behavior of a staticmethod, but since you are simply assigning a already defined function to a class attribute you cannot use the decorator syntax.
But since decorators are just normal functions which take a function as parameter and return a wrapped function, this:
class Child(Base):
property2 = staticmethod(function)
is equivalent (*) to this:
class Child(Base):
#staticmethod
def property2():
function()
Further improvements
I would suggest a small additional modification to your Base class:
Rename property2 and mark it not as abstractproperty but as abstractstaticmethod(**).
This will help colleagues (and eventually yourself) to understand better what kind of implementation is expected in the child class.
class Base(object):
__metaclass__ = ABCMeta
#abstractstaticmethod
def staticmethod1(self):
pass
(*) well, more or less. The former actually assigns function to property2, the latter creates a new static method which delegates to function.
(**) abstractstaticmethod is deprecated since Python 3.3, but since you are also using abstractproperty I wanted to be consistent.

Storing reference to unbound method

I was trying to store reference to unbound method and noticed that it is being automatically bound. See example below. Is there more elegant way to store unbound method in the class without binding it?
def unbound_method():
print("YEAH!")
class A:
bound_method = unbound_method
unbound_methods = [unbound_method]
a = A()
a.unbound_methods[0]() # succeeds
a.bound_method() # fails
# TypeError: unbound_method() takes 0 positional arguments but 1 was given
This is not a standard "do you know about #staticmethod?" question.
What I'm trying to achieve is provide a way for children of the class provide another handler or certain situations. I do not control the unbound_method itself, it is provided from some library.
def unbound_method_a():
print("YEAH!")
def unbound_method_b():
print("WAY MORE YEAH!")
class A:
bound_method = unbound_method_a
class B(A):
bound_method = unbound_method_b
a = A()
a.bound_method() #fails
# print("YEAH!")
b = B()
b.bound_method() #fails
# print("WAY MORE YEAH!")
It can be achieved by wrapping the unbound method some dummy object like array, or in a bound method, just to drop self reference like this:
def unbound_method_a():
print("YEAH!")
def unbound_method_b():
print("WAY MORE YEAH!")
class A:
def call_unbound_method(self):
return unbound_method_a()
class B(A):
def call_unbound_method(self):
return unbound_method_b()
a = A()
a.call_unbound_method()
# print("YEAH!")
b = B()
b.call_unbound_method()
# print("WAY MORE YEAH!")
Not as far as I know. Would it be so bad if you just replace
a.bound_method()
with
A.bound_method()
?
I can't think of a situation in which the first one can't be replaced by the second one.

Instance variables and functions

I made a post here functions and class attributes (python)
When you define
a class attribute and gave it a function like this:
example 1
def add_age(cls,age):
cls.yrs_old = age
return cls
class Test:
age = add_age
a = Test()
a.age(5)
print(a.yrs_old)
self is automatically passed as the first argument of the add_age function.
However toying around with it more doing the same thing
but this time passing the function as an instance attribute like this:
example 2
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
a = Test(test_func)
print(a.func())
Answers in the linked post stated that all functions in the class are automatically passed a self if the class is instantiated like this:
a = Test(test_func)
Now what's strange here is had I put test_func in a class attribute it works just like my very first example.
If you pass the function in the constructor/init like this:
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
and call it like this:
a = Test(test_func)
print(a.func())
a.func is suddenly acting like a static method as opposed to example 1 where the function defined inside the class attribute becomes a regular class method.
What's going on?.
I thought all functions within a class are implicitly passed a self argument.
After the body of the class statement is evaluated, the metaclass wraps each function in a descriptor which takes care of the distinction between instance, class, and static methods. When you assign the function to an instance attribute, you bypass that machinery, so that the attribute refers to a plain function object.
From documentation -
Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok.
This means that only methods that are assigned to classes are instance methods for the instances of the class.
Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> b = A()
>>> b.a()
Hmm
>>> b.a
<bound method A.a of <__main__.A object at 0x006D13D0>>
But as soon as you assign a separate function object to the instance variable, it is no longer an instance method , since is is not defined at the class level, it is only defined for that particular instance , Example -
>>> def c():
... print("Hello")
...
>>> b.a = c
>>> b.a()
Hello
>>> b.a
<function c at 0x0017B198>
As you can see, when you directly assigned the function to the instance variable (instead of assigning it to class variable , it is now a normal instance attribute, that references a function object, and not an instance method.
You can also assign functions to class variables after the definition of class , and the instances would automatically get them as instance methods, Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> def c(a):
... print("Hello - ", a)
...
>>> b = A()
>>> A.b = c
>>> b.b
<bound method A.c of <__main__.A object at 0x006D13D0>>
>>> b.b()
Hello <__main__.A object at 0x006D13D0>

Naming conventions for class method vs instance method

I have two methods, one for the individual Instance, and one for every Instance in that class:
class MasterMatches(models.Model):
#classmethod
def update_url_if_any_matches_has_one(cls):
# apply to all instances, call instance method.
def update_url_if_any_matches_has_one(self):
# do something
Should I name these the same? Or, what is a good naming convention here?
The question of using the same names can be clarified by understanding how decorators work.
#dec
def foo(x):
print(x)
translates to
def foo(x):
print(x)
foo = dec(foo)
In your example the decorator syntax can be expanded to
class MasterMatches(models.Model):
def update_url_if_any_matches_has_one(cls):
# apply to all instances, call instance method.
update_url_if_any_matches_has_one = classmethod(update_url_if_any_matches_has_one)
def update_url_if_any_matches_has_one(self):
# do something
The former implementation of update_url_if_any_matches_has_one will be overwritten by the latter.
Usually use self declaration style. #classmethod use only if method not works with class instance fields.
Function decorated as #classmethod takes the first argument is the class type, while normal method takes instance of object.
class A:
#classmethod
def a(cls):
print(cls)
def b(self):
print(self)
a = A()
a.a()
a.b()
# Output:
# <class '__main__.A'>
# <__main__.A object at 0x03FC5DF0>
It can be useful if you have a static class fields. The to access therm you don't need explicitly specify the class name. But you don't get access to instance fields. Example:
class A:
field = 1
#classmethod
def a(cls):
print(cls.field)
def b(self):
self.field = 2
print(self.field, A.field)
a = A()
a.a()
a.b()
# Outputs:
# 1
# 2 1

Categories

Resources