Confusion With OOP in Python - python

I'm trying to learn OOP in Python but I'm confused about some parts.
class Song(object):
def __init__(self, lyrics):
self.lyrics = lyrics
def sing_me_a_song(self):
for line in self.lyrics:
print line
def print_x(self):
print x
happy_bday = Song(["Happy birthday to you,",
"I don't want to get sued",
"So I'll stop right there"])
bulls_on_parade = Song(["They'll rally around the family",
"With pockets full of shells"])
happy_bday.sing_me_a_song()
bulls_on_parade.sing_me_a_song()
x = Song(4)
x.print_x()
print_x() returns:
<__main__.Song object at 0x7f00459b4390>
instead of 4. So I try adding x to the parameters for __init__ and print_x, and changing print x to print self.x in the print_x function plus adding self.x = x to init, but it returns this:
TypeError: __init__() takes exactly 3 arguments (2 given)
I honestly don't know what's gone awry here. But any help would be hugely beneficial to me finally understand OOP.

This is less an OOP issue and more of a scoping issue. Lets examine a very cut down version.
class Song(object):
def __init__(self, lyrics):
self.lyrics = lyrics
def print_x(self):
print x
From here we instantiate x (in the local scope):
>>> x = Song(4)
Now, before we do anything, lets examine x:
>>> print x.lyrics
4
The reason is that when you called Song(4) the value 4 is determined to be lyrics by its position.
And when we call print_x:
>>> x.print_x()
<__main__.Song object at 0x7f00459b4390> # Or some other memory address
The reason is that the only x that Python is aware of is the local x we just made.
What happens when we start again and make y:
>>> y = Song(4)
>>> print y.print_x ()
NameError: global name 'x' is not defined
No x exists to print and it throws an exception.

I think you are trying like
def __init__(self, lyrics, x):
self.lyrics = lyrics
self.x = x
...
def print_x(self):
print self.x
By this way it will produce TypeError: init() takes exactly 3 arguments (2 given)
You can find the error from this.
When you creating Song instance like
happy_bday = Song(["Happy birthday to you,",
"I don't want to get sued",
"So I'll stop right there"])
You have to pass the value of x to the __init__().Thats why the error is showing.This can done by
happy_bday = Song(["Happy birthday to you,",
"I don't want to get sued",
"So I'll stop right there"],
'x-value')
OR
Set default value for x
def __init__(self, lyrics, x="default value"):
self.lyrics = lyrics
self.x = x

Related

Problems Appear in Visual Studio Code

I am a beginner at coding and currently in a python class at my university. I'm having some issues pop up in the "Problems" tab in Visual Studio Code. It wants me to put a self argument in my function inside a class, however when I call the class in my other file the code works it just displays problems. I tried putting self into the classes code and then everything stops working. The code was copied during the lecture and is the same code as my professor's using the same coding platform however his code doesn't show problems. I am attaching 2 screen shoots since the code is from 2 different files. I'm guessing something in my settings is causing this to happen. I have a similar thing happen when I do something like.for i in something: it will tell me i isn't defined yet the code will work.
Screenshot 1
Screenshot 2
Inside a class, that has non static methods, there must be a self variable.
class Math_Functions:
def addition(self, x, y): # there must be a self
return x + y
#... so on
This seems to be a static method, but VC may require you to still provide a self reference. Because python (at this specific case) does not require the self reference by default, when imported your code works file, but standalone, VC may tell you that you need an extra self there. Also you have an extra comma in addition after your y parameter.
Just simply pass in self into each of the methods (math functions), although it may work without it, it's good practice to do this if you want to continue learning OOP.
class mathFunctions:
def add(self, x, y): #Avoids the problem in VS
return x + y
It is required to pass in self for it to work when creating an object using a class using init, for example:
class employee:
def __init__(self, firstName, lastName, age):
self.firstName = firstName
self.lastName = lastName
self.age = age
def fullName(self):
return self.firstName + ' ' + self.lastName
#creating an object within the class
John = employee('John', 'Smith', '26')
#Returns "John Smith"
print(John.fullName())
Hope this might help, good luck on your python class!
This is a response to both replies. Putting self into the code creates a different problem and the code won't run because it tells me I am "missing 1 required positional argument: 'y'"
Three solutions:
First One:
In settings.json file add this configuration:
"python.linting.pylintArgs": [
"--disable=all",
"--enable=F,E,unreachable,duplicate-key,unnecessary-semicolon,global-variable-not-assigned,unused-variable,binary-op-exception,bad-format-string,anomalous-backslash-in-string,bad-open-mode",
"--disable=no-self-argument"
],
The first and second settings are the default settings of pylint, the third one can suppress the warning.
Second One:
Change your code to these:
main:
from calc import Math_functions as mf
print("Welcom to Calculator!")
x = int(input('Number: '))
y = int(input('Number: '))
op = input('Operation: ')
f = mf(x, y)
if op == '+':
print(f.addition())
elif op == '-':
print(f.subtraction())
elif op == '*':
print(f.multiplication())
elif op == '/':
print(f.division())
calc:
class Math_functions:
def __init__(self, x, y):
self.x = x
self.y = y
def addition(self):
return self.x+self.y
def subtraction(self):
return self.x-self.y
def multiplication(self):
return self.x*self.y
def division(self):
return self.x/self.y
Third One:
Change your code to these:
main:
import calc as mf
print("Welcom to Calculator!")
x = int(input('Number: '))
y = int(input('Number: '))
op = input('Operation: ')
if op == '+':
print(mf.addition(x, y))
elif op == '-':
print(mf.subtraction(x, y))
elif op == '*':
print(mf.multiplication(x, y))
elif op == '/':
print(mf.division(x, y))
calc:
def addition(x, y):
return x+y
def subtraction(x, y):
return x-y
def multiplication(x, y):
return x*y
def division(x, y):
return x/y

How can i add my objects into a list to choose them randomly to blit onto the screen?

When i try to put my objects into a list, i can not get an output with object names, it gives a weird output like "_ main _.object at 0x029E7210". I want to select my objects randomly to blit ONE of them onto the screen. But i could not figure this out.
car_main = pygame.image.load("car_main.png")
car_red_ = pygame.image.load("car_red.png")
car_blue = pygame.image.load("car_blue.png")
class cars:
def __init__(self,x,y,car_type,w=50,h=100,s=5):
self.x = x
self.y = y
self.w = w
self.h = h
self.s = s
self.car_type = car_type
def draw(self):
dp.blit(self.car_type,(self.x,self.y))
car1 = cars(x,y,car_main)
car2 = cars(x,y,car_red)
car3 = cars(x,y,car_blue)
car_list = [car1,car2,car3]
rc = random.choice(car_list)
print(rc)
# output> __main__.object at 0x02A97230
When I change
car_list = [car1,car2,car3] with;
car_list = [car1.car_type,car2.car_type,car3.car_type]
# output > Surface(50x100x32 SW)
But I want to see an output as my object names. Not as a string type ("car_main"). I want to get an output as the object name (car_main) directly. Because in the main loop, i will choose one of them to blit onto the screen everytime when the loop renews itself.
You need to define __str__ for your class Car to let it properly handle object to string:
class Car:
def __str__(self):
for k, var in globals().items():
if var == self:
return k
# default
return "Car"
Note1: Usually use uppercased Car for a class and car for an instance.
Note2: Look up variable strings in globals is not reliable. You may not want to make all variables global, and manually search them in scope is tedious. Actually why don't you give your Car a name attribute? Then you nicely have:
class Car:
def __init__(self, name):
self.name=name
def __str__(self):
return self.name
car = Car(name='first car')
print(car) # 'first car'
More read about "magic methods": https://rszalski.github.io/magicmethods/#representations
Add a __str()__ magic method to your car class like so:
def __str__(self):
return f'car with x of {self.x}, y of {self.y}, and type of {self.car_type}'

python - Calling function within same Class

Please see the sample code:
a = [1,2,3,4,5] # main list
b = [4,5,6] #variable list nr1
c = [1,2] #variable list nr2
class union(object):
def __init__(self, name):
self.name = name
def common_numbers(self, variable_list):
self.variable_list = variable_list
for x in self.name:
if x in self.variable_list:
yield(x)
def odd_numbers(self, odds):
self.odds = odds
for x in self.variable_list:
if not x % 2 == 0:
yield x
''' I receive: builtins.AttributeError: 'union' object has no attribute 'variable_list'.'''
x = union(a)
print(list(x.odd_numbers(c)))
I am trying to understand how to call other function within same class. As you can see, I am trying to find odd numbers from common_numbers function.
Please understand this is sample work. I know there are plenty of solutions with or withouth using classes to get propriet result. But in this case, I don't need result, I would really appretiate if you could help me understand the calling other function within class. Sorry for my English and Thank you in advance.
You're getting the error because you never actually define self.variable_list. It's only defined once you call common_numbers(), but you never do that. You can define it when initiating:
class union(object):
def __init__(self, name, variable_list):
self.name = name
self.variable_list = variable_list
def common_numbers(self):
for x in self.name:
if x in self.variable_list:
yield(x)
x = union(a, b)
print list(x.odd_numbers(c))
or after initiating, but before calling odd_numbers:
class union(object):
def __init__(self, name):
self.name = name
def common_numbers(self):
for x in self.name:
if x in self.variable_list:
yield(x)
x = union(a)
x.variable_list = b
print list(x.odd_numbers(c))

Passing variables between functions in a class

Trying to split up and tokenize a poem (or haiku in this case), which is more of a way to teach myself how to use nltk and classes than anything else. When I run the code below, I get a Name Error: name 'psplit' is not defined even though (my thinking is) that it's defined when I return it from the split function. Can anyone help me figure out what's going wrong under the hood here?
import nltk
poem = "In the cicada's cry\nNo sign can foretell\nHow soon it must die"
class Intro():
def __init__(self, poem):
self.__poem = poem
def split(self):
psplit = (poem.split('\n'))
psplit = str(psplit)
return psplit
def tokenizer(self):
t = nltk.tokenize(psplit)
return t
i = Intro(poem)
print(i.split())
print(i.tokenizer())
There are some issues in your code:
In the split method you have to use self.__poem to access the the poem attribute of your class - as you did in the constructor.
The psplit variable in the split method is only a local variable so you can just use it in this method and nowhere else. If you want to make the variable available in the tokenize method you have to either pass it as an argument or store it as an additional attribute:
...
def tokenizer(self, psplit):
t = nltk.tokenize(psplit)
return t
...
psplit = i.split()
print(i.tokenizer(psplit))
Or:
def __init__(self, poem):
...
self._psplit = None
...
def split(self):
self._psplit = (poem.split('\n'))
self._psplit = str(psplit)
def tokenizer(self):
t = nltk.tokenize(self._psplit)
return t
...
i.split()
print(i.tokenizer())
In addition make sure your indentation is correct.

python accessing class variables in instances from derived classes

I have gone through many answers and not found what I understand to be the answer. This is a test program based on a problem in my real program. I want to have a class variable that I can change and have the change apply to all instances of a class, but not to a similar class even with the same form.
As is probably obvious, I am defining a class variable for X in line 3 and a class variable for Y in line 9. I am trying to access these in lines 23-25.
My model is
#! /usr/bin/python -t
class X:
clsvar = "Animal"
def show(self):
clsvar
def chg(self,creature):
clsvar = creature
class Y:
clsvar = "Plant"
def show(self):
clsvar
def chg(self,creature):
clsvar = creature
class A(X):
pass
class B(X):
pass
class C(Y):
pass
a = A()
b = B()
c = C()
print "1 " + a.show()
print "2 " + b.show()
print "3 " + c.show()
a.chg( "Dog")
print "4 " + a.show()
print "5 " + b.show()
print "6 " + c.show()
My result is
Traceback (most recent call last):
File "180.py", line 23, in ?
print "1 " + a.show()
File "180.py", line 5, in show
clsvar
NameError: global name 'clsvar' is not defined
I would have thought clsvar would appear in any of the derived classes and not need to be global. I am obviously being stupid here but I have tried this dozens of ways without success.
Incidentally, I was able to do this in Ruby.
#! /usr/bin/ruby -w
class X
##clsvar = "Animal"
def self.show
##clsvar
end
def self.chg(creature)
##clsvar = creature
end
end
class Y
##clsvar = "Plant"
def self.show
##clsvar
end
def self.chg(creature)
##clsvar = creature
end
end
class A < X
A.show
end
class B < X
B.show
end
class C < Y
C.show
end
a = A
b = B
c = C
puts "1 " + a.show
puts "2 " + b.show
puts "3 " + c.show
a.chg( "Dog")
puts "4 " + a.show
puts "5 " + b.show
puts "6 " + c.show
And the output is:
1 Animal
2 Animal
3 Plant
4 Dog
5 Dog
6 Plant
To access a class variable you have to do this:
MyClass.clsvar
Or even this:
an_instance.clsvar
The latter works only if the instance does not have any instance variable called clsvar.(*)
Python is not like Java. Take into account that, unlike Java, Python does have global variables. For example:
a = 1
class MyClass:
a = 2
def show(self):
print(a)
The show method would print 1, since it refers to the global variable a, and not to MyClass.a.
(*) A note on this. You can access MyClass.var using self.var and it's fine if you do not modify it. But setting the value is not equivalent. If you want to set a class variable then you have to use MyClass.var = value and not an_instance.var = value. The latter would create a new instance variable called var with value value, so MyClass.var will still have the old value.
By the way, I do not know Ruby, but I think the ## syntax is used to access class variables so that's why it works.
Finally, your code is incorrect. You probably want to put some return statements in those methods, otherwise you'll get some TypeErrors when executing it.

Categories