I'm using 'an illustrated guide to learning python 3' to learn python. Chapter 21 is about classes. In this chapter it uses 'self' aparently incorrectly? I tried writing my own code for an example, and it didn't work, so I input the example code and, surprisingly, it did not work either.
class CorrectChair:
'''blah'''
max_occupants = 4
def __init__(self, id):
self.id = id
self.count = 0
def load(self, number):
new_val = self.check(self.count + number)
self.count = new_val
def unload(self, number):
new_val - self._check(self.count - number)
self.count = new_val
def _check(self, number):
if number < 0 or number > self.max_occupants:
raise ValueError('Invalid count:{}'.format(number))
return number
It errors out into:
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
CorrectChair.load(1)
TypeError: load() missing 1 required positional argument:
'number'
It appears to not be recognizing the self argument.. How can I fix this? Googling has not helped, every example I see makes it look like this should work.
It should be adding (number) to self.count, instead it ignores that its self referential, and asks for a 2nd argument.
You must create an instance and call the methods on it:
replace CorrectChair.load(1) with:
c_chair = CorrectChair(some_id)
c_chair.load(1)
The load function is actually a object method. In the Python world, the first parameter of a object method always points to the instance, which will implicitly passes to the method before invoking. To call a object method, you first need to create a object, then invoke that method by the dot syntax. Autually
e.g
id = 3
newCorrectChair = CorrectChair(id)
# self is implicitly passed here, this style stems from C.
CorrectChair(id).load(10)
If you was trying to write a class method, you have to add a #classmethod decorator.
class CorrectChair:
# Blah...
#classmethod
def load(cls, num):
# do something
return
If you was trying to write a static function, you should decorate that method with #staticmethod decorator.
class CorrectChair:
# Blah...
#staticmethod
def load(cls, num):
# do something
return
The error is showing that you're trying to call a method straight from the class,
while the method expects an object reference as well.
Before you make any call for any of those methods that include 'self', you need to create an instance of that class first
In your case, the code should be:
correct_chair = CorrectChair(id)
correct_chair.load(1)
Comparing to the method in your class -
correct_chair corresponds to self, and 1 corresponds to 'number' in the method
def load(self, number):
new_val = self.check(self.count + number)
self.count = new_val
Related
If I have a class ...
class MyClass:
def method(arg):
print(arg)
... which I use to create an object ...
my_object = MyClass()
... on which I call method("foo") like so ...
>>> my_object.method("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 1 positional argument (2 given)
... why does Python tell me I gave it two arguments, when I only gave one?
In Python, this:
my_object.method("foo")
... is syntactic sugar, which the interpreter translates behind the scenes into:
MyClass.method(my_object, "foo")
... which, as you can see, does indeed have two arguments - it's just that the first one is implicit, from the point of view of the caller.
This is because most methods do some work with the object they're called on, so there needs to be some way for that object to be referred to inside the method. By convention, this first argument is called self inside the method definition:
class MyNewClass:
def method(self, arg):
print(self)
print(arg)
If you call method("foo") on an instance of MyNewClass, it works as expected:
>>> my_new_object = MyNewClass()
>>> my_new_object.method("foo")
<__main__.MyNewClass object at 0x29045d0>
foo
Occasionally (but not often), you really don't care about the object that your method is bound to, and in that circumstance, you can decorate the method with the builtin staticmethod() function to say so:
class MyOtherClass:
#staticmethod
def method(arg):
print(arg)
... in which case you don't need to add a self argument to the method definition, and it still works:
>>> my_other_object = MyOtherClass()
>>> my_other_object.method("foo")
foo
In simple words
In Python you should add self as the first parameter to all defined methods in classes:
class MyClass:
def method(self, arg):
print(arg)
Then you can use your method according to your intuition:
>>> my_object = MyClass()
>>> my_object.method("foo")
foo
For a better understanding, you can also read the answers to this question: What is the purpose of self?
Something else to consider when this type of error is encountered:
I was running into this error message and found this post helpful. Turns out in my case I had overridden an __init__() where there was object inheritance.
The inherited example is rather long, so I'll skip to a more simple example that doesn't use inheritance:
class MyBadInitClass:
def ___init__(self, name):
self.name = name
def name_foo(self, arg):
print(self)
print(arg)
print("My name is", self.name)
class MyNewClass:
def new_foo(self, arg):
print(self)
print(arg)
my_new_object = MyNewClass()
my_new_object.new_foo("NewFoo")
my_bad_init_object = MyBadInitClass(name="Test Name")
my_bad_init_object.name_foo("name foo")
Result is:
<__main__.MyNewClass object at 0x033C48D0>
NewFoo
Traceback (most recent call last):
File "C:/Users/Orange/PycharmProjects/Chapter9/bad_init_example.py", line 41, in <module>
my_bad_init_object = MyBadInitClass(name="Test Name")
TypeError: object() takes no parameters
PyCharm didn't catch this typo. Nor did Notepad++ (other editors/IDE's might).
Granted, this is a "takes no parameters" TypeError, it isn't much different than "got two" when expecting one, in terms of object initialization in Python.
Addressing the topic: An overloading initializer will be used if syntactically correct, but if not it will be ignored and the built-in used instead. The object won't expect/handle this and the error is thrown.
In the case of the sytax error: The fix is simple, just edit the custom init statement:
def __init__(self, name):
self.name = name
Newcomer to Python, I had this issue when I was using the Python's ** feature in a wrong way. Trying to call this definition from somewhere:
def create_properties_frame(self, parent, **kwargs):
using a call without a double star was causing the problem:
self.create_properties_frame(frame, kw_gsp)
TypeError: create_properties_frame() takes 2 positional arguments but 3 were given
The solution is to add ** to the argument:
self.create_properties_frame(frame, **kw_gsp)
As mentioned in other answers - when you use an instance method you need to pass self as the first argument - this is the source of the error.
With addition to that,it is important to understand that only instance methods take self as the first argument in order to refer to the instance.
In case the method is Static you don't pass self, but a cls argument instead (or class_).
Please see an example below.
class City:
country = "USA" # This is a class level attribute which will be shared across all instances (and not created PER instance)
def __init__(self, name, location, population):
self.name = name
self.location = location
self.population = population
# This is an instance method which takes self as the first argument to refer to the instance
def print_population(self, some_nice_sentence_prefix):
print(some_nice_sentence_prefix +" In " +self.name + " lives " +self.population + " people!")
# This is a static (class) method which is marked with the #classmethod attribute
# All class methods must take a class argument as first param. The convention is to name is "cls" but class_ is also ok
#classmethod
def change_country(cls, new_country):
cls.country = new_country
Some tests just to make things more clear:
# Populate objects
city1 = City("New York", "East", "18,804,000")
city2 = City("Los Angeles", "West", "10,118,800")
#1) Use the instance method: No need to pass "self" - it is passed as the city1 instance
city1.print_population("Did You Know?") # Prints: Did You Know? In New York lives 18,804,000 people!
#2.A) Use the static method in the object
city2.change_country("Canada")
#2.B) Will be reflected in all objects
print("city1.country=",city1.country) # Prints Canada
print("city2.country=",city2.country) # Prints Canada
It occurs when you don't specify the no of parameters the __init__() or any other method looking for.
For example:
class Dog:
def __init__(self):
print("IN INIT METHOD")
def __unicode__(self,):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
When you run the above programme, it gives you an error like that:
TypeError: __init__() takes 1 positional argument but 6 were given
How we can get rid of this thing?
Just pass the parameters, what __init__() method looking for
class Dog:
def __init__(self, dogname, dob_d, dob_m, dob_y, dogSpeakText):
self.name_of_dog = dogname
self.date_of_birth = dob_d
self.month_of_birth = dob_m
self.year_of_birth = dob_y
self.sound_it_make = dogSpeakText
def __unicode__(self, ):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
print(id(obj))
If you want to call method without creating object, you can change method to static method.
class MyClass:
#staticmethod
def method(arg):
print(arg)
MyClass.method("i am a static method")
I get this error when I'm sleep-deprived, and create a class using def instead of class:
def MyClass():
def __init__(self, x):
self.x = x
a = MyClass(3)
-> TypeError: MyClass() takes 0 positional arguments but 1 was given
You should actually create a class:
class accum:
def __init__(self):
self.acc = 0
def accumulator(self, var2add, end):
if not end:
self.acc+=var2add
return self.acc
In my case, I forgot to add the ()
I was calling the method like this
obj = className.myMethod
But it should be is like this
obj = className.myMethod()
Part A
I want to do some checking on arguments to a class instantiation and possibly return None if it doesn't make sense to even create the object.
I've read the docs but I don't understand what to return in this case.
class MyClass:
def __new__(cls, Param):
if Param == 5:
return None
else:
# What should 'X' be?
return X
What should X be in return X?
It cannot be self because the object doesn't exist yet so self is not a valid keyword in this context.
Part B
Tied to my question, I don't understand the need to have the cls parameter.
If you call the constructor of MyClass - var = MyClass(1) - won't cls always be MyClass?
How could it be anything else?
According to the docs, cls in object.__new__(cls[, ...]) is:
. . .the class of which an instance was requested as its first
argument.
(I'm assuming you are using Python 3 because you provided a link to Python 3 docs)
X could be super().__new__(cls).
super() returns the parent class (in this case it is simply object). Most of the times when you are overriding methods you will need to call the parent class's method at some point.
See this example:
class MyClass:
def __new__(cls, param):
if param == 5:
return None
else:
return super().__new__(cls)
def __init__(self, param):
self.param = param
And then:
a = MyClass(1)
print(a)
print(a.param)
>> <__main__.MyClass object at 0x00000000038964A8>
1
b = MyClass(5)
print(b)
print(b.param)
>> None
Traceback (most recent call last):
File "main.py", line 37, in <module>
print(b.param)
AttributeError: 'NoneType' object has no attribute 'param'
You could just return the instance of cls like this return object.__ new__(cls). Because every class is subclass of object, you can use that as a object creator for your class. The returnes object is passed as a first argument to the __init__() with the any number of positional argument or any number of keyword argument you passed to new. There you will create instance variable assigning those values.
I'm trying to define a method which takes a python list as one of its input parameters. By contrast regular functions have no problem accepting lists as input parameters. How Come?
# Simple function that works
def func(param1, param2):
for item in param1:
print item+" "+param2
var1 = ['sjd', 'jkfgljf', 'poipopo', 'uyuyuyu']
var2 = 'is nonsense'
func(var1, var2)
# Simple function produces the following output:
# sjd is nonsense
# jkfgljf is nonsense
# poipopo is nonsense
# uyuyuyu is nonsense
If I try to get a similar effect with a method inside a class like this:
# Simple class
class test():
def __init__(self):
pass
def test_method(par1, par2):
for itm in par1:
print itm+" "+par2
# This executes with no error
obj = test()
# This fails
obj.test_method(var1, var2)
# Error message will be:
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: test_method() takes exactly 2 arguments (3 given)
Seems like I'm missing something very basic, any help will be greatly appreciated.
If you want test_method to have access to data members in your class, then you need to pass self, as in:
def test_method(self, par1, par2):
If test_method does not need to have access to data members in your class, then declare it as a static method:
#staticmethod
def test_method(par1, par2):
For reference, let's say you have a class that holds a number and you want to return said number in a method, and you have another method that gives the product of two numbers, but doesn't depend on anything in your class. Here's how you would do it:
class myClass(object):
def __init__(self, num):
self.number = num
def getNum(self):
return self.number
#staticmethod
def product(num1,num2):
return num1*num2
if __name__ == "__main__":
obj = myClass(4)
print obj.getNum()
print myClass.product(2,3)
Prints:
4
6
Just change:
def test_method(par1, par2):
to
def test_method(self, par1, par2):
I ran the code below, by calling the function in the constructor
First --
>>> class PrintName:
... def __init__(self, value):
... self._value = value
... printName(self._value)
... def printName(self, value):
... for c in value:
... print c
...
>>> o = PrintName('Chaitanya')
C
h
a
i
t
a
n
y
a
Once again I run this and I get this
>>> class PrintName:
... def __init__(self, value):
... self._value = value
... printName(self._value)
... def printName(self, value):
... for c in value:
... print c
...
>>> o = PrintName('Hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
NameError: global name 'printName' is not defined
Can I not call a function in the constructor? and whay a deviation in the execution of similar code?
Note: I forgot to call a function local to the class, by using self (ex: self.printName()). Apologize for the post.
You need to call self.printName since your function is a method belonging to the PrintName class.
Or, since your printname function doesn't need to rely on object state, you could just make it a module level function.
class PrintName:
def __init__(self, value):
self._value = value
printName(self._value)
def printName(value):
for c in value:
print c
Instead of
printName(self._value)
you wanted
self.printName(self._value)
It probably worked the first time because you had another function printName in a parent scope.
What you want is self.printName(self._value) in __init__, not just printName(self._value).
I know this is an old question, but I just wanted to add that you can also call the function using the Class name and passing self as the first argument.
Not sure why you'd want to though, as I think it might make things less clear.
class PrintName:
def __init__(self, value):
self._value = value
PrintName.printName(self, self._value)
def printName(self, value):
for c in value:
print(c)
See Chapter 9 of the python manuals for more info:
9.3.4. Method Objects
Actually, you may have guessed the answer: the special thing about methods is that the object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x). In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method’s object before the first argument.
I work on something in Seattle Repy which is a restricted subset of Python. Anyway, I wanted to implement my own Queue that derives from a list:
class Queue(list):
job_count = 0
def __init__(self):
list.__init__(self)
def appendleft(item):
item.creation_time = getruntime()
item.current_count = self.job_count
self.insert(0, item)
def pop():
item = self.pop()
item.pop_time = getruntime()
return item
Now I call this in my main server, where I use my own Job class to pass Jobs to the Queue:
mycontext['queue'] = Queue()
# ...
job = Job(str(ip), message)
mycontext['queue'].appendleft(job)
The last line raises the following exception:
Exception (with type 'exceptions.TypeError'): appendleft() takes exactly 1 argument (2 given)
I'm relatively new to Python, so could anyone explain to me why it would think that I gave appendleft() two arguments when there obviously was only one?
You must enter the self reference in each function definition:
def appendleft(self, item):
Python automatically passes SELF (ie the current object) as the first argument, so you'd need to change the function definition for appendleft to:
def appendleft(self, item):
This is also true for other function definitions within a class. They all require SELF as the first parameter in the function definition, so:
def pop():
would need to be:
def pop(self):
Python passes the object itself as the first argument to it's methods. You need to modify your class methods to take the mandatory first argument, conventionally (a strong convention that is) named self.
Read this - http://docs.python.org/tutorial/classes.html