how to move function to class? python - python

I would like to to use service functions in a classmethod, where the service function is defined somewhere else. I want to be dynamic, so I can define different functions in different situations. I've tried this:
def print_a():
print 'a'
class A:
func = print_a
#classmethod
def apply(cls):
cls.func()
A.apply()
Yet I receive this error:
unbound method print_a() must be called with A instance as first argument (got nothing instead)
Any ideas how to make it work?

you can use call
def print_a():
print 'a'
class A:
func = print_a.__call__
#classmethod
def apply(cls):
cls.func()
A.apply()
Output
a

Related

Class variable reference to function changes to instancemethod

I'm trying to call an external function via a class variable. The following is a simplification of my real code:
def func(arg):
print(arg)
class MyClass(object):
func_ref = None
#classmethod
def setUpClass(cls):
#MyClass.func_ref = func
cls.func_ref = func
#staticmethod
def func_override(arg):
print("override printing arg...")
MyClass.func_ref(arg)
if __name__ == "__main__":
print(type(func))
print(type(MyClass.func_ref))
MyClass.setUpClass()
print(type(MyClass.func_ref))
MyClass.func_override("hello!")
The above code produces the following output:
[~]$ python tmp.py
<type 'function'>
<type 'NoneType'>
<type 'instancemethod'>
override printing arg...
Traceback (most recent call last):
File "tmp.py", line 20, in <module>
MyClass.func_override("hello!")
TypeError: func_override() takes exactly 2 arguments (1 given)
The situation seems to be unchanged if I use MyClass in place of cls within the classmethod setUpClass().
I would expect the type of MyClass.func_ref to be function after the assignment in setUpClass() which explains the TypeError I get when I try to call it. Why is the type of func_ref being changed to instancemethod when the value I assigned to it is of type function?
This only seems to be an issue in Python 2. Python 3 behaves as I would expect.
How do I get calls to the static method MyClass.func_override() to call func()?
UPDATE
I was able to get the above to work by applying the following patch:
## -14,7 +14,7 ## class MyClass(object):
def func_override(arg):
print("override printing arg...")
func(arg)
- MyClass.func_ref.__func__(arg)
+ MyClass.func_ref(arg)
if __name__ == "__main__":
print(type(func))
While the above works, its not at all clear to me why I needed to do this. I still don't understand why the type of func_ref ends up an instancemethod when I assigned to it a value of type function.
Just put the function through a staticmethod as follows:
#classmethod
def setUpClass(cls):
#MyClass.func_ref = func
cls.func_ref = staticmethod(func)
There's no need to play with #-based decorators in this case as you want to modify how the method is bound to MyClass, not the general definition of func.
Why is this necessary? Because, when you assign a method to class, Python assumes you'll want to refer to an instance (via self) or the class (via cls). self, unlike this in JS, is only a naming convention, so when it sees arg it assumes it got an instance, but you passed a string in your call.
So, as as Python cares, you might have as well have written def func(self):. Which is why the message says unbound method func() must be called with MyClass 👉instance👈 as first argument.
staticmethod means, "please leave this alone and don't assume an instance or a class in the first variable".
You can even dispense with the setUpClass entirely:
class MyClass(object):
func_ref = staticmethod(func)
BTW: In 2021, 16 months past EOL, Python 2.7 has all the subtle fagrance of moldy gym socks. Except less safe, virologically-speaking.
When func_ref is called, it's expecting a self argument, just like any other normal (instance) class method (see this question and answers for discussions why). You can either add a self argument to func or make func a static method:
#staticmethod
def func(arg):
print(arg)
>>> MyClass.setUpClass()
>>> MyClass.func_override("hello!")
override printing arg...
hello!
Note that in either case func is now not normally callable as a regular function:
>>> func('what does this do?')
TypeError: 'staticmethod' object is not callable
If you need func to be usable as a regular function, you can wrap it with another, qualifying function and use the wrapper in MyClass:
def func(arg):
print(arg)
#staticmethod
def func_wrapper(arg):
func(arg)
class MyClass(object):
#classmethod
def setUpClass(cls):
cls.func_ref = func_wrapper # use wrapper function
>>> MyClass.setUpClass()
>>> MyClass.func_override("success!")
override printing arg...
success!

Unable to call function with self

I'm writing a simple code snippet here, but unable to run the code
class test:
def __init__(self):
pass
def functiona(self, a):
b = a+0
print(b)
def functionb(self):
a = 5
self.functiona(a)
test.functionb('abc')
It errors out with "AttributeError: 'str' object has no attribute 'functiona'" Unable to call it with self. However, if I provide test.functiona(a) it works fine.
Few of other code samples works with self.function, how to solve this issue
test.functionb('abc') is a function call on the class, not on an instance.
I suppose it works if you do test().functionb('abc')?
The difference is:
In your case, you call the function object on the class. As it is not a staticmethod or classmethod, it is called with self = 'abc', a string. This string hasn't a method functiona().
In my case, the call operates on a class instance. Here, self is set to the instance you just created – and you get an error because it doesn't know where to pass the 'abc'.
Problem lies in the call test.functionb('abc'). You are not using object of the class to call the method. So, the self parameter is not passed.
Python considers, the first parameter to be self, and you passed 'abc' which is a string.
Use it like test().functionb('abc') , then the default first argument becomes the object of test - like functionb(test_ob, 'abc').
you can add the decorator #classmethod and then call it like you did
class test:
def __init__(self):
pass
#classmethod
def functiona(self, a):
b = a+0
print(b)
#classmethod
def functionb(self):
a = 5
self.functiona(a)
>>> test.functiona(1001)
>>> 1001
>>> test.functionb()
>>> 5
Problem lies in the call test.functionb('abc'). You are not using object of the class to call the method. So, the self parameter is not passed. Python considers, the first parameter to be self, and you passed 'abc' which is a string.
Use it like test().functionb('abc') , then the default first argument becomes the object of test - like functionb(test_ob, 'abc').

Python - Is it possible to define an instance method inside another instance method?

Is it possible to do something like this? (This syntax doesn't actually work)
class TestClass(object):
def method(self):
print 'one'
def dynamically_defined_method(self):
print 'two'
c = TestClass()
c.method()
c.dynamically_defined_method() #this doesn't work
If it's possible, is it terrible programming practice? What I'm really trying to do is to have one of two variations of the same method be called (both with identical names and signatures), depending on the state of the instance.
Defining the function in the method doesn't automatically make it visible to the instance--it's just a function that is scoped to live within the method.
To expose it, you'd be tempted to do:
self.dynamically_defined_method = dynamically_defined_method
Only that doesn't work:
TypeError: dynamically_defined_method() takes exactly 1 argument (0 given)
You have to mark the function as being a method (which we do by using MethodType). So the full code to make that happen looks like this:
from types import MethodType
class TestClass(object):
def method(self):
def dynamically_defined_method(self):
print "two"
self.dynamically_defined_method = MethodType(dynamically_defined_method, self)
c = TestClass()
c.method()
c.dynamically_defined_method()

Reference instance method outside class definition

I'm trying to pass a method as an argument outside of the definition of a class. However, since it's not defined in that scope, it doesn't work. Here's what I'd like to do:
def applyMethod(obj, method):
obj.method()
class MyClass():
def myMethod(self):
print 1
a = MyClass()
#works as expected
a.myMethod()
#"NameError: name 'myMethod' is not defined"
applyMethod(a, myMethod)
myMethod is only defined in the namespace of MyClass. Your code could look like this:
def applyMethod(obj, method):
method(obj)
class MyClass():
def myMethod(self):
print 1
a = MyClass()
a.myMethod()
applyMethod(a, MyClass.myMethod)
Now you're referencing myMethod from the namespace it exists in, and calling the equivalent of obj.myMethod() from the applyMethod function.
That's all you need - the instance.method() is just syntactic sugar for ClassName.method(instance), so I just rewrote the applyMethod function to work without the syntactic sugar, i.e. be passed in as the raw MyClass.myMethod, then gave it an instance of MyClass as its first argument. This is what the a.myMethod() syntax is doing in the backend.

Python TypeError regarding arguments

This is my code:
class bla:
def function1():
print 1
def function2():
bla.function1()
x = bla()
x.function2()
I don't understand why I get the error "TypeError: function2() takes no arguments (1 given)" as I don't seem to be passing any argument to function2.
Regular methods are called with an implicit self reference to their object - otherwise they wouldn't be able to access any data members of x.
They should always be declared like so:
class bla:
def function1(self):
print 1
if you want them to operate on the object (self is loosely equivalent to the this pointer in C++, for example).
Alternatively, if you don't care about the object (so you're really just using the class to group some functions together), you can make them static like so:
class bla:
#staticmethod
def function1():
print 1
#staticmethod
def function2():
bla.function1()
In fact, that's the only way you can call bla.function1() without an instance of bla from your function2.
That's cause your calling your function as a method and that automatically binds the method's object as the first argument to your function.
Either do:
bla.function2() #a function call
or:
class bla:
#normal and correct way to define class methods - first argument is the object on which the method was called
def function1(self):
print 1
def function2(self):
self.function1()
You have to type:
class bla:
def function1(self):
print 1
def function2(self):
self.function1()
self (a reference to the object on which the method is called) is passed as the first parameter to each method. The name of this first variable, "self" is just a common convention.
You have to pass the argument self to the functions function1 and function2. See the Python Classes documentation. So your code would be
class Bla: # Notice capitalization.
def function1(self):
print 1
def function2(self):
bla.function1()
x = Bla()
x.function2()
See also the answers to the question Why do you need explicitly have the “self” argument into a Python method?.
Basically in Python a call to a member function (like function1)
x = Bla()
x.function1()
is translated into
Bla.function(x)
self is used to refer to the instance of the class x.

Categories