Reference instance method outside class definition - python

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.

Related

Why class definition needs more information of its attributes than what is needed in a function definition?

def myfunc():
return x
x = 10
print(myfunc())
The above codes work, where the free variable x does not need to be defined when I define myfunc.
However, the following codes do not work:
class myclass:
func = extfunc
def extfunc(self):
return 'hello'
Here I need to move the definition of extfunc before the class definition, in order to make the codes work.
Why does class definition need more information of its attributes than what is needed for a free variable in a function definition?
This code:
def myfunc():
return x
defines a function, but doesn't execute the code inside it where x is until/unless myfunc is called. The body of the function isn't evaluated when the function definition is evaluated, it's evaluated later when the function is called.
In contrast, in this code:
class myclass:
func = extfunc
the class definition is evaluated in order to create the class, as described in the docs here. So func = extfunc is evaluated as part of class definition in order to assign a value to the func variable in the class scope. func is like a static member in languages that use that terminology.
A more direct comparison would be this:
class myclass:
def example(self):
return x
There, return x isn't evaluated until or unless example is called.
See also this example in the documentation:
Attribute references use the standard syntax used for all attribute references in Python: obj.name. Valid attribute names are all the names that were in the class’s namespace when the class object was created. So, if the class definition looked like this:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively.
In your example, myclass.func would be a valid reference immediately after the class definition, so func = extfunc must be evaluated during the class definition, unlike the body of a function.

Is it possible to call a static method from withing another static method

Is it possible to call a static method from within another static method?
I tried this:
class MyClass(object):
#staticmethod
def static_method_1(x):
x = static_method_2(x)
print x
#staticmethod
def static_method_2(x):
return 2*x
This returns
NameError: name 'static_method_2' is not defined
Staticmethods are called via the class: MyClass.static_method_2(x).
You probably don't want a staticmethod at all, but a classmethod. These are called the same way but get a reference to the class, which you can then use to call the other method.
class MyClass(object):
#classmethod
def static_method_1(cls, x):
x = cls.static_method_2(x)
print x
#classmethod
def static_method_2(cls, x):
return 2*x
Note, in Python you wouldn't ever do this. There's usually no reason to have a class unless it is storing state. These would probably both be best as standalone functions.
A static method must be invoked via the class that defines it; otherwise, that's just about the only difference between it and a regular function.
#staticmethod
def static_method_1(x):
x = MyClass.static_method_2(x)
print x
The reason is that the name static_method_2 isn't defined in the global scope, or in any other non-local scope (remember, a class does not define a new scope). The static method is simply an attribute of MyClass, and has to be accessed as such.

how to move function to class? 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

Binding class methods to external functions

I get an error when trying to bind a class method to a function. Why?
def foo():
print "Hello world"
class something(object):
bar = foo
test = something()
test.bar()
TypeError: foo() takes no arguments (1 given)
Also, if I am unable to modify foo, can I do this adaptation from within the class definition?
A simple way to do it is to wrap the function in a staticmethod inside A:
class A():
bar = staticmethod(foo)
>>> test = A()
>>> test.bar()
Hello world
A class method in python always takes at least one argument, usually called self. This example is taken from the official Python tutorial:
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)
class C:
f = f1
def g(self):
return 'hello world'
h = g
Note that both methods, regardless of whether they are defined outside or inside of the class, take self as an argument.
Edit: If you really can't change your foo function, then you can do something like this:
>>> def foo():
... print "Hello World"
...
>>> class something(object):
... def bar(self): foo()
...
>>> test = something()
>>> test.bar()
Hello World
When you call a class method this way you pass the class instance as the first parameter.
When you call test.bar what in fact happens is more like bar(test). You pass an argument to the method.
Class methods all have a first argument of the instance of the class. So add a parameter to your function and it would work.
The initial def creates a function object named foo. Since it's outside any class, it's just a function that takes no arguments. The assignment bar = foo just gives the new name test.bar to that same function object. The call test.bar(), however, assumes that bar is a class method, and passes the object test as the first argument to the method (the one that you would normally call self). You could call it as a static method with something.bar() and not get the error.
Remember that when a python class calls one of it's methods it will pass in a self argument. You need to account for that in your code:
def foo(self):
print ("Hello world")
class something(object):
bar = foo
test = something()
test.bar()
You can read all about classes in the Python Documentation
The easiest workaround to not passing in a self that I can think of is:
def foo():
print ("Hello world")
class something(object):
bar = [foo] # contains pointer to static method
test = something()
test.bar[0]() # calls the static method

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