Binding class methods to external functions - python

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

Related

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.

Advantages of Using MethodType in Python

What are the advantages of using MethodType from the types module? You can use it to add methods to an object. But we can do that easily without it:
def func():
print 1
class A:
pass
obj = A()
obj.func = func
It works even if we delete func in the main scope by running del func.
Why would one want to use MethodType? Is it just a convention or a good programming habit?
In fact the difference between adding methods dynamically at run time and
your example is huge:
in your case, you just attach a function to an object, you can call it of course but it is unbound, it has no relation with the object itself (ie. you cannot use self inside the function)
when added with MethodType, you create a bound method and it behaves like a normal Python method for the object, you have to take the object it belongs to as first argument (it is normally called self) and you can access it inside the function
This example shows the difference:
def func(obj):
print 'I am called from', obj
class A:
pass
a=A()
a.func=func
a.func()
This fails with a TypeError: func() takes exactly 1 argument (0 given),
whereas this code works as expected:
import types
a.func = types.MethodType(func, a) # or types.MethodType(func, a, A) for PY2
a.func()
shows I am called from <__main__.A instance at xxx>.
A common use of types.MethodType is checking whether some object is a method. For example:
>>> import types
>>> class A(object):
... def method(self):
... pass
...
>>> isinstance(A().method, types.MethodType)
True
>>> def nonmethod():
... pass
...
>>> isinstance(nonmethod, types.MethodType)
False
Note that in your example isinstance(obj.func, types.MethodType) returns False. Imagine you have defined a method meth in class A. isinstance(obj.meth, types.MethodType) would return True.

Why doesn't Python raise an error on namespace collision?

The following Python code executes normally without raising an exception:
class Foo:
pass
class Foo:
pass
def bar():
pass
def bar():
pass
print(Foo.__module__ + Foo.__name__)
Yet clearly, there are multiple instances of __main__.Foo and __main__.bar. Why does Python not raise an error when it encounters this namespace collision? And since it doesn't raise an error, what exactly is it doing? Is the first class __main__.Foo replaced by the second class __main__.Foo?
In Python everything is an object - instance of some type. E.g. 1 is an instance of type int, def foo(): pass creates object foo which is an instance of type function (same for classes - objects, created by class statement are instances of type type). Given this, there no difference (at the level of name binding mechanism) between
class Foo:
string = "foo1"
class Foo:
string = "foo2"
and
a = 1
a = 2
BTW, class definition may be performed using type function (yeah, there is type type and built-in function type):
Foo = type('Foo', (), {string: 'foo1'})
So classes and functions are not some different kind of data, although special syntax may be used for creating their instances.
See also related Data Model section.
The Foo class is effectively being re-defined further down the script (script is read by the interpreter from top to bottom).
class Foo:
string = "foo1"
class Foo:
string = "foo2"
f = Foo()
print f.string
prints "foo2"
The second definition replaces the first one, as expected if you think at classes as elements in the "types dictionary" of the current namespace:
>>> class Foo:
... def test1(self):
... print "test1"
...
>>> Foo
<class __main__.Foo at 0x7fe8c6943650>
>>> class Foo:
... def test2(self):
... print "test2"
...
>>> Foo
<class __main__.Foo at 0x7fe8c6943590>
>>> a = Foo()
>>> a.test1()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Foo instance has no attribute 'test1'
>>> a.test2()
test2
>>>
here you can clearly see that the "definition" of Foo changes (Foo points to different classes in memory), and that it's the last one that prevails.
Conceptually this is just rebinding a name. It's no different from this:
x = 1
x = 2
and I'm sure you would not want that to be an error.
In compiled and some interpreted languages there is a clear seperation between definition, declaration and execution. But in python it's simpler. There are just statements!
Python EXECUTES your script/program/module as soon as it is invoked. It may help, to see def and class as "syntactic sugar". E.g. class is a convenient wrapper around Foo = type("class-name", (bases), {attributes}).
So python executes:
class Foo #equivalent to: Foo = type("class-name", (bases), {attributes})
class Foo
def bar
def bar
print(Foo.__module__ + Foo.__name__)
which boils down to overwriting the names Fooand bar with the latest "declaration". So this just works as intended from a python-pov - but maybe not as you intended it! ;-)
so it's also a typical error for developers with a different background to misunderstand:
def some_method(default_list = []):
...
default_list is a singleton here. Every call to some_method usese the same default_list, because the list-object is created at first execution.
Python doesn't enter the function-body, but only executes the signature/head as soon as it begins parsing.

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.

How can I annotate a class in pypy?

I am using pypy to translate some python script to C language.
Say that I have a python class like this:
class A:
def __init__(self):
self.a = 0
def func(self):
pass
I notice that A.func is a unbound method rather than a function so that it cannot be translated by pypy. So I change the code slightly:
def func(self):
pass
class A:
def __init__(self):
self.a = 0
A.func = func
def target(*args):
return func, None
Now func seems to be able to be translated by pypy. However when I try translate.py --source test.py, an exception [translation:ERROR] TypeError: signature mismatch: func() takes exactly 2 arguments (1 given) is raised. I notice that it might because I haven't annotate self argument yet. However this self have type A, so how can I annotate a class?
Thank you for your reading and answer.
Essentially PyPy's entry point is a function (accepting sys.argv usually as an argument). Whatever this function calls (create objects, call methods) will get annotated. There is no way to annotate a class, since PyPy's compiled code does not export this as API, but rather as a standalone program.
You might want to for example:
def f():
a = A()
a.func()
or even:
a = A()
def f():
a.func()
in which case a is a prebuilt constant.
Are you wanting a staticmethod or a classmethod?

Categories