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

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.

Related

__init__() missing 1 required positional argument it reads self as a parameter

I'm new at programming and I'm learning Python. The code should be very simple. The goal should be implement a calculator that does additions between numbers.
It returns this error:
init() missing 1 required positional argument: 'number_2'
So it's like it reads self as a parameter, but I can't figure out why.
I'm using Linux Ubuntu 19 as operative system.
Here's my code:
class Calculator:
def __init__(self, number_1, number_2):
self.number_1=number_1
self.number_2=number_2
def add(self):
print(f"{number_1}+{number_2}={number_1+number_2}")
if __name__=="__main__":
c=Calculator('Casio')
c.add(2,3)
It isn't reading self as a parameter here, but 'Casio' which it is storing as number_1. As the error message reads, it is missing number 2. If you want add() to be able to take arbitrary values, you will need to add them as arguments to that method rather than to the __init__() function.
You have to pass parameters to the add function and not to __init__ which instantiates the class.
class Calculator:
def __init__(self, name):
self.name=name
def add(self, number_1, number_2):
print(f"{number_1}+{number_2}={number_1+number_2}")
if __name__=="__main__":
c=Calculator('Casio')
c.add(2,3)
When you are initializing the object 'c', you are running the init method, and you therefore need to pass in both parameters to the init method. So, you are required to give both 'number_1' and 'number_2' when you create the object. You passed in only'Casio', which python is interpreting as 'number_1'. Python is also interpreting that there is no 'number_2'. Here are some solutions:
1: You could make the add() function have the two arguments that init has ('number_1' and 'number_2') and have the only arguments for init be self and 'name'. Then, you could have the init method only do self.name = name and nothing else. 2: You could make the arguments 'number_1' and 'number_2' optional by making a default variable for them if you do not enter it:
def __init__(self, number_1="1", number_2="2"):

Unbound method error

I'm trying to understand the self keyword in python classes. Ergo I made up a simple class:
class Question(object):
"""A class for answering my OO questions about python.
attributes: a_number"""
def __init__():
print "You've initialized a Question!"
def myself_add(self, new_number):
self.a_number += new_number
And in a __main__ function below the class definition I'm running the code
my_q = Question
print my_q
my_q.a_number = 12
print 'A number:',my_q.a_number
my_q.myself_add(3)
print 'A number:',my_q.a_number
The result I'm getting (including error) is
<class '__main__.Question'>
A number: 12
Traceback (most recent call last):
File "question.py", line 21, in <module>
my_q.myself_add(3)
TypeError: unbound method myself_add() must be called with Question instance as first argument (got int instance instead)
I'm trying to understand why the method myself_add is considered unbound. What does that mean? And why doesn't it know that I'm calling it on an instance of the Question class? Also, why doesn't my __init__ print happen?
In order to create an instance you need to write Question() not Question.
__init__ should take at least self argument
You may also want to initialize self.a_number in __init__ or put in into the class body, otherwise the call of myself_add will fail if you do not execute my_q.a_number = 12
The "self" argument can be anything, but is called self by convention.
It's passed in for you by python on default.
You can try running this simple code snippet to see what I mean:
class A(object):
def __init__(self):
pass
def check(self, checked):
print self is checked
and then from the command line:
a=A()
a.check(a)
You can take this further like this:
In [74]: b=A()
In [75]: a.check(b)
False
So here I've created two instances of A, one called a one called b. Only when comparing and instance of a's self to a itself is the statement true.
Hopefully that makes sense.
Specifically in your case, your class Question needs to be instantiated (Question()) and your __init__ needs an argument (self by convention).
On when a function is bound, you might find this answer helpful:
What is the difference between a function, an unbound method and a bound method? (but might be a bid advanced)
First of all , when you initialize an instance of an class you use opening and closing parenthesis which calls the __init__ method by convention.
my_q = Question()
However, in your case your __init__ method is not bound to an object using self, so if you run as is it will give error,
my_q = Question()
TypeError: __init__() takes no arguments (1 given)
>>>
You need to initialize __init__ with self so that instance can make implicit call to the object.
def __init__(self):
Now , the program runs smoothly
First print is the my_q object itself.
>>>
You've initialized a Question!
<__main__.Question object at 0x028ED2F0>
A number: 12
A number: 15

Is it possible to run a print() inside a method without the #staticmethod attribute?

I come from a .NET and Javascript background and I'm working my way up to learn Python (for Raspberry Pi).
Right now I'm trying to figure out OOP in Python and the use of methods and classes. But having a little issue with #staticmethod
class Car(object):
"""description of class"""
def __init__(self, make, model):
self.make = make
self.model = model
#staticmethod
def makeFirstNoise():
print("Vrooooommm!")
def makeSecondNoise():
print("Mweeeeeeeeeh!")
This is how I implement my class and try to run both methods.
from Car import Car
mustang = Car('Ford', 'Mustang')
mustang.makeFirstNoise()
mustang.makeSecondNoise()
This is the output:
Vrooooommm!
Traceback (most recent call last):
File "D:\Dev\T\PythonHelloWorld\PythonHelloWorld\PythonHelloWorld.py", line 5, in <module>
mustang.makeSecondNoise()
TypeError: makeSecondNoise() takes 0 positional arguments but 1 was given
So question, why can't I execute the second method without my staticmethod attribute? This seems to work if I just return the text directly like this:
def makeSecondNoise():
return "Mweeeeeeeh!"
print(mustang.makeSecondNoise())
The reason makeSecondNoise is causing an error is because it is automatically passed one argument, self, because it's not declared as a staticmethod. self is the instance of the class that the function was called on. This is ultimately causing the error because makeSecondNoise isn't coded to accept any parameters; it'd be like doing this:
def something():
...
something("Foo")
Here's an example of how self works:
>>> class Car:
... def makenoise(self):
... print(self)
...
>>> mustang = Car()
>>> mustang.makenoise()
<__main__.Car object at 0x0000000005498B38> # We can see that "self" is a reference to "mustang"
Your problem isn't related to print (I couldn't get your example without print to work either) - it's related to the automatic passing of the self argument.
In Python all method calls (besides classmethods and staticmethods) explicitly passed object instance as first argument. Convention is to name this argument self. This explicit argument should be included in method signature:
class Car(object):
def makeSecondNoise(self): # note that method takes one argument
print("Mweeeeeeeeeh!")
After that you may call your method without any problems.
mustang = Car('Ford', 'Mustang')
mustang.makeSecondNoise()
In Java, this (which represents instance object) is passed implicitly to methods - this is a source of your confusion.

How does Python know the instance variable without defining __init__?

Let us consider the following example:
class X:
def run(self):
print("An example.")
X().run()
The output is:
> An example.
But when we omit the reference to the instance:
class X:
def run():
print("An example.")
X().run()
The output is:
TypeError: run() takes 0 positional arguments but 1 was given
When we instantiate the class, __ new __ gets called and the instance is created, ok. But how it requires an instance without defining __ init __? (I'm surprised because, I've always written __ init __ thinking that it was responsible for defining the convention / self name for referencing the variable). I'm confused.
When you call instance.run() if implicitly calls run(instance) which is why you're receiving the error:
TypeError: run() takes 0 positional arguments but 1 was given
That's also the reason why instance methods should have self as the first argument.
Second, you're using the old way of declaring a class - class X:
The new way1 is class X(object): but regardless if you're using the new/old annotation, the call X() will return an instance of the class, even in case you didn't define __init__.
And third, if you want to make it a class method you can either do what Pynchia suggested in the comment above (annotate the method with #staticmethod) or declare the method as a class method by specifying that the first argument is cls and annotating it as a class-method:
class X:
#classmethod
def run(cls):
print "a"
X.run() # prints a
1. According to Mark's comment below, in Python 3 we return to the "old way" of declaring a class. Good to know - thanks Mark!

Calling a python method

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).

Categories

Resources