Calling a python method - python

I have a file notifications.py and comments.py. notifications has a class GetNotifications with a function getNotifs(arg1, arg2). I'd like to call this method in my comments.py, so I do:
from notifications import GetNotifications
Then I create an instance of the class:
getNotifsInstance = GetNotifications()
Then I try to call getNotifs:
notifsDictionary = getNotifsInstance.getNotifs(arg1, arg2)
However, I'm getting the error:
TypeError: getNotifs() takes at most 2 arguments (3 given)
Why is it saying I'm giving it 3 arguments when I'm only giving it 2?

You're giving it three arguments: when you call an instance method, the instance is passed as the first parameter. You should add an argument self as the first parameter of the method.

You've most probably forgotten about the self argument when declaring getNotifs():
def getNotifs(self, arg1, arg2):
...

Why is it saying I'm giving it 3 arguments when I'm only giving it 2?
Simply because you are accessing the function getNotifys as a member function of object getNotifsInstance. The first argument to any member function is (self) the object reference itself.

In the class you can define three kinds of methods:
class A(object):
def instance_method(*args):
print '%d arguments given' % len(args)
#classmethod
def class_method(*args):
print '%d arguments given' % len(args)
#staticmethod
def static_method(*args):
print '%d arguments given' % len(args)
And when you invoke them on the instance, you will get additional argument passed to instance method (which will be instance itself) and class method (which will be a class of the instance):
>>> a = A()
>>> a.instance_method('a', 'b')
3 arguments given
>>> a.class_method('a', 'b')
3 arguments given
>>> a.static_method('a', 'b')
2 arguments given
In your case it is probably self (the instance itself), although results would be similar if you would decorate your method with classmethod decorator (in that case you would get a class passed as the first argument).

Related

Different ways of calling methods in Python, How they work?

class Test():
def a(self):
return 6
Test_instance=Test()
print(Test.a(Test_instance))
print(Test_instance.a())
print(Test.a(Test_instance,3))
print(Test_instance.a(3))
The code above give me the following result:
6
6
TypeError: a() takes 1 positional argument but 2 were given
If I exclude the line print(Test.a(Test_instance,3)) I get the same print out. But Isn't a a method of both, Test class and Test_instance object? When I write Test_instance.a() I supose I'm giving one argument to the a() method of Test_instance and when I write Test.a(Test_instance) I supose I'm giving two arguments to the a() method of Test. But the error says that Test.a(Test_instance,3) gives 2 arguments. Shouldn't it be 3?
But Isn't "a" a method of both, Test object and Test_instance object?.
It is indeed.
When I write Test_instance.a() I supose im giving one argument to "a" method of Test_instance and When I write Test.a(Test_instance) I supose Im giving two arguments to "a" method of Test.
Right. But that applies only to method calls on instances.
But the error says that Test.a(Test_instance,3) gives 2 arguments. Shouldn't be 3?
No. It is 2, as you call the method directly on the class and not via the instance.
Calling a method directly on the class calls it as it is.
Calling a method on the instance, however, prepends the instance as first argument.
The reason for this are the internals.
If I do instance.X, X is a class member and implements the __get__() method (Descriptor protocol), internally X.__get__() is called and the result of it is produced for instance.X.
And functions implement the descriptor protocol in order to create an instance method object, which is reponsible for prepending the instance as first argument.
So, in your example, you as well can do
>>> print(Test_instance.a)
<bound method Test.a of <__main__.Test instance at 0x7f6e232e3690>>
>>> Test.a.__get__(Test_instance, Test)
<bound method Test.a of <__main__.Test instance at 0x7f6e232e3690>>
Both these "bound methods" are what I referred to as "instance method object".
Other example for this:
class Desc(object):
def __get__(self, a, b):
print('Calling __get__(%r, %r, %r)' % (self, a, b))
return a or b
class Test():
x = Desc()
>>> Test.x
Calling __get__(<__main__.Desc object at 0x7f6e232e1810>, None, <class __main__.Test at 0x7f6e232ea050>)
<class __main__.Test at 0x7f6e232ea050>
>>> inst = Test()
>>> inst.x
Calling __get__(<__main__.Desc object at 0x7f6e232e1810>, <__main__.Test instance at 0x7f6e232e3c80>, <class __main__.Test at 0x7f6e232ea050>)
<__main__.Test instance at 0x7f6e232e3c80>
What happens here?
Test.x takes the x member of Test and calls its .__get__(x, None, Test). This method returns b (which is set to Test), as the a parameter is None.
inst.x takes the x member of Test and calls its .__get__(x, inst, Test). This in turn returns a.
In a similar way, methods are set up to work.
Properties as well use the same mechanism, BTW.
If you use the #classmethod and #staticmethod decorators, they wrap the functions into some wrapping objects which have a slightly different .__get__() behaviour.
When you do instance.a(), instance is impliciltly passed as the first argument. When you do instance.a(3), you give two arguments (the implicit first instance, and the explicit 3), which your function cannot handle.
When you do Test.a(), Test is not passed as an implicit first argument because a is not defined as a class method.
If you do
class Test():
#classmethod
def a(self):
return 6
and then try Test.a(123) would give you the error a() takes 1 positional argument but 2 were given
Or Test.a(Test_instance,3), you'll get the error you expect.

Difference between having #staticmethod and not having it

In Python 3.x what can I do with bar() that I cannot do with foo()?
class A:
def foo():
print("some code")
#staticmethod
def bar():
print("some code")
Note: I initially forgot to specify self as an argument to foo(), but I'm leaving the mistake there, since the answers andress that.
a staticmethod is a method that does not require an object as its first parameter. This means it is a method useful to the class itself and all instantiations of it, rather than just instances of it (object initialized as A().
What this means in practical terms, is that Python does not implicitly send the object itself as a parameter. Your first method will break once you call it:
>>> a.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes 0 positional arguments but 1 was given
This is because Python supplies object methods with the object itself as a first parameter. Hence the ubiquitous self argument:
def foo(self): #Proper signature
On the other hand,
A.bar()
will work just fine, and so will
a.bar()
The object is not supplied as a first argument. Use staticmethods for methods that are supposed to be helpful to the class and its instances, but do not require knowledge of either. Usually I use these as utility functions.
Note there is a third version, a classmethod, which is similar to a regular method in that it accepts a first parameter by default - the class of the caller. In this case the minimal signature is
#classmethod
def operateOnClass(cls):
Use this to make changes that affect all instances, such as changing class variables.
bar() can be called from an uninstantiated class object. foo() needs to be fed self as an argument, and as such can only be called from an object already declared as an instance of class A

How self variable works,when class is assigned to instance variables

I came across following piece of code in robot framework.
variable are assigned with different class name.
https://github.com/robotframework/robotframework/blob/master/src/robot/variables/variables.py
def __init__(self):
self.store = VariableStore(self)
self._replacer = VariableReplacer(self)
self._finder = VariableFinder(self.store)
To understand how does above assignment work,i wrote following piece of code,which is throwing error when using self
class Foo(object):
def __init__(self):
pass
def addition(self):
return 5
class boo(object):
def __init__(self):
self.word = Foo(self)
def multiply(self):
return self.word.addition()
if __name__ == '__main__':
b = boo()
print(b.multiply()) #TypeError: __init__() takes 1 positional argument but 2 were given
When using following combination in boo() class,getting the correct output i.e. 5.
self.word=Foo;return self.word.addition(self)
self.word=Foo();return self.word.addition()
The issue is def __init__(self) inside the Foo class. You have as the parameter self, but, just like any other method, it doesn't have to be specifically passed in. So in the boo constructor, when you say self.word = Foo(self), you're actually calling __init__ of Foo with two arguments; the inherent self and the self of boo you're passing. That explains why you're getting the error of __init__() takes 1 positional argument but 2 were given.
There are a couple things wrong with your interpretation of what is happening. First, let's look at the code and the error you're getting:
b = boo()
print(b.multiply()) #TypeError: __init__() takes 1 positional argument but 2 were given
Your comment seems to imply you think the error is happening when you call b.multiply(). However, when you look at the stack trace you'll see it happens when you do b = boo():
Traceback (most recent call last):
File "/tmp/junk.py", line 16, in <module>
b = boo()
File "/tmp/junk.py", line 10, in __init__
self.word = Foo(self)
TypeError: __init__() takes 1 positional argument but 2 were given
Note that the stack trace is telling you precisely what the problem is. You've defined Foo.__init__ to take a single parameter but two are being given. Why are two being given? When you create an instance of a class in python, python automatically passes in the self parameter. In other words, if you do b=boo(), your function will automatically get self without you having to explicitly pass it in. This is a very fundamental part of python's design. It has nothing to do with robot framework.
In trying to replicate the robot code, you've created a class named Foo that takes a single parameter named self. However, the VariableStore object takes two parameters: self and variables:
class VariableStore(object):
def __init__(self, variables):
...
Remember, python will automatically pass in self, so the first argument you pass in will get associated with the variables parmeter.
Consider this line of code:
self.store = VariableStore(self)
Based on how VariableStore is defined, it is exactly the same as this:
self.store = VariableStore(variables=self)
Notice how self is not the same as the self parameter of the __init__. It is passed to the variables parameter. This is because python automatically passes self as the first parameter when constructing a class.

Besides instance method, static method, and class method, is there a fourth type of method in Python?

In python, there are 3 types of methods: instance method, static method, and class method. But let's look at the following example:
class foo:
def inst_method(self, *args, **kwargs): pass
#classmethod
def class_method(cls, *args, **kwargs): pass
#staticmethod
def static_method(*args, **kwargs): pass
def unknown_method(*args, **kwargs):
print('What? ')
def another_unknown():
print('What??? ')
foo.unknown_method() # OK
foo().unknown_method() # OK
foo.another_unknown() # OK
foo().another_unknown() # !!!
Grammatically, the definition of unknown_method is valid and it can be invoked on the class. But which type does this method belong to?
Thanks to the answers I have received, it is for sure that unknown_method is still an instance method because self is just an optional name; it can be any other names.
But what about another_unknown? It can only be invoked on the class, not on an instance.
This is still a regular instance method. You can demonstrate this like so:
>>> class Foo:
mystery_method(*args, **kwargs):
print(*args, sep = '\n')
>>> f = Foo(1, 'bar', None)
>>> f.mystery_method()
<__main__.Foo object at XXXX>
1
bar
None
As you can see, we only passed in 3 arguments, but four arguments have been printed. The first member of *args has been bound to the object instance, just as with any other regular instance method.
The usage of self as the first argument is just a convention.
The other method example you gave (with no arguments) will just raise an exception because Python will try to pass the class instance to the method, but the method accepts zero arguments. It is still an instance method.
self is just a label. The first argument passed into these functions is always the instance, whether you call it self or not. You could call it this or blerg, but the first argument passed to the function will always be the instance.
Short version: no, there's no unknown_method. It's just another type of inst_method.
EDIT
other_unknown won't work at all because one argument will be passed in. You'll get a TypeError.

How to create a decorator function with arguments on python class?

I want to create a decorator function to operate on a python class, with the ability to pass additional arguments. I want to do that before the class gets instantiated. Here is my approach:
def register(x,a):
print x,a
#register(5)
class Foo(object):
pass
with x being the class and a the additional argument. But I get a
TypeError: register() takes exactly 2 arguments (1 given)
What I want is some way to get hold of the class Foo and additional arguments at the time the class is defined, before the class is instantiated.
You need to do it this way:
def makeDeco(a):
def deco(cls):
print cls, a
return cls
return deco
>>> #makeDeco(3)
... class Foo(object):
... pass
<class '__main__.Foo'> 3
You can use functools.wraps and so forth to spruce it up, but that is the idea. You need to write a function that returns a decorator. The outer "decorator-making" function takes the a argument, and the inner decorator function takes the class.
The way it works is that when you write #makeDeco(3) it calls makeDeco(3). The return value of makeDeco is what is used as the decorator. That is why you need makeDeco to return the function you want to use as the decorator.

Categories

Resources