access python class variable from method - python

I am new to Python, and am trying to figure out how to access the global variable count within the repr method in the Student class below:
class Student(object):
count = 0
def __init__(self, **kwargs):
self.name = kwargs.get("name")
self.age = kwargs.get("age")
Student.count += 1
def __repr__(self):
try:
return "name: %s, age: %d" % (self.name, self.age)
except TypeError:
print "student number: %d" % (Student.count)
When I create an instance, such as student = Student, and the try to print the variable student, I get the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "school.py", line 23, in __repr__
print "student number: %d" % (Student.count)
NameError: global name 'count' is not defined
Thanks.

You can access class properties as well using self. Not that in your case, your error handling in __repr__ isn’t necessary. In a method, self will always be correct (unless you really mess something up), so you can just assume that you can access self properly. As you initialize name and age in the initializator, you can also assume that both properties exist:
class Student (object):
count = 0
def __init__ (self, **kwargs):
self.name = kwargs['name']
self.age = kwargs['age']
self.count += 1
def __repr__ (self):
return "name: %s, age: %d" % (self.name, self.age)
Instead of accepting just **kwargs, it might be a good idea to actually ask for name and age as proper parameter in the initializator. Since you do expect name and age to be specified, incorrectly calling your constructor will fail early that way:
def __init__ (self, name, age, **kwargs): # **kwargs in case you want to accept more
self.name = name
self.age = age
self.count += 1
That all being said, the error you show shouldn’t happen. There shouldn’t be an exception being thrown in __repr__, and Student.count should be accessible as well. So you are probably calling __repr__ in some weird way. The proper way would be something like this:
a = Student(name='Peter', age=15)
print(repr(a))

You should use self.count instead of Student.count inside the class. You do not need to use Student.count because you are still inside the class, self refers to anything inside the class. It doesn’t need to be inside the __init__ function. For example:
class RandomClass:
random_var = 0
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
another_var = 3
def __str__(self):
print(“Printing variables: %d and %d” % (self.random_var, self.another_var))#using self.random_var, not RandomClass.random_var

Related

How can I pass **kwargs in __init__() constructor in python?

I'm trying to write a program where I am trying to pass **kwargs in init() method. After that
when I m trying to make a instance variable inside the constructor(init() method ) , I cant able to make . How can I do this ?
Here is my code :
class Student:
def __init__(self,**kwargs):
self.name = name
self.age = age
self.salary = salary
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student('John',25,15000)
st2 = Student('Doe',25,1500000)
st.show_salary()
st2.show_salary()
**kwargs expects arguments to be passed by keyword, not by position. Once you do that, you can access the individual kwargs like you would in any other dictionary:
class Student:
def __init__(self, **kwargs):
self.name = kwargs.get('name')
self.age = kwargs.get('age')
self.salary = kwargs.get('salary')
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student(name='John', age=25, salary=15000)
st2 = Student(name='Doe', age=25, salary=1500000)
st.show_salary()
st2.show_salary()
If you want to pass these arguments by position, you should use *args instead.
kwargs is created as a dictionary inside the scope of the function. You need to pass a keyword which uses them as keys in the dictionary. (Try running the print statement below)
class Student:
def __init__(self, **kwargs):
#print(kwargs)
self.name = kwargs["name"]
self.age = kwargs["age"]
self.salary = kwargs["salary"]
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student(name = 'John',age = 25, salary = 15000)
st2 = Student(name = 'Doe',age = 25,salary = 1500000)
st.show_salary()
st2.show_salary()
Though you can do this as some of the answers here have shown, this is not really a great idea (at least not for the code you are showing here). So I am not going to answer the subject line question you have asked, but show you what the code you seem to be trying to write should be doing (and that is not using kwargs). There are plenty of places where using kwargs is the best solution to a coding problem, but the constructor of a class is usually not one of those. This is attempting to be teaching, not preaching. I just do not want others coming along later, seeing this question and thinking this is a good idea for a constructor.
The constructor for your class, the __init__(), generally should be defining the parameters that it needs and expects to set up the class. It is unlikely that you really want it to take an arbitrary dictionary to use as its parameter list. It would be relatively rare that this is actually what you want in your constructor, especially when there is no inheritance involved that might suggest you do not know what the parameters are for some reason.
In your __init__() itself you clearly want the parameters name, age and salary, yet without them in the parameter list it is not clear to the caller that you do. Also, your usage of it does not seem to imply that is how you expect to use it. You call it like this:
st = Student('John',25,15000)
and so you do not even seem to want named parameters.
To handle the call structure you have shown the __init__() would look like this:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
If you want to be be able to call it without some parameters such that it uses defaults for the ones left out, then it should be like this:
def __init__(self, name=None, age=None, salary=None):
self.name = name
self.age = age
self.salary = salary
It seems very unlikely that the kwargs approach is really what you want here, though obviously you can code it that way as other answers have shown.
Perhaps you are just trying to figure out how to use kwargs, and that is fine, but a different example would be better if that is the case.

Why can't i run a method from the same class in python?

Topic Closed
So I'm learning OOP in python and wanted to test my knowledge. That's what i did
class Student:
def cantBeStudent():
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
cantBeStudent()
student1 = Student(age=18, education=2)
I get name_error when i try to call cantBeStudent(). It says that cantBeStudent is not defined. I can't find my answer on google so I came here.
Edit: Also when i comment out whole cantBeStudent i get SyntaxError on def init
You need to add self to the method invocation and declaration:
class Student:
def cantBeStudent(self): # need self
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent() # need self
student1 = Student(age=18, education=2)
OR
You need to invoke cantBeStudent as a static method like so:
class Student:
def cantBeStudent(): # no self as first argument, therefore static method
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
Student.cantBeStudent() # because declaration has no self,
# cantBeStudent belongs to entire Student class
student1 = Student(age=18, education=2)
When you construct a class, methods that you define must take the instance as the first argument. The class instance is referred to as self (though you could call it anything you wanted):
class X:
def __init__(self, number):
self.number = number
def add_number(self, arg):
self.number += arg
You see this when you define __init__. All other functions* work this way as well. When you call them like
instance = X(1)
instance.add_number(3)
It's analogous to doing:
instance = X(1)
X.add_number(instance, 3)
It's just calling the method against the instance will automatically pass self for you. When you call that method inside the instance, you need to specify the instance you are calling against, it's just this is called self instead of instance:
class X:
~snip~
def add_number(self, arg):
self.number += arg
def increment_number(self):
# note the self.<method>
self.add_number(1)
Again, this would be identical to the call:
instance = X(1)
X.increment_number(instance)
Because the instance gets passed in so that it can be called with the appropriate method
* All other functions that are not decorated with #staticmethod or #classmethod
You should provide self to any function that you want it to be counted as an object's method. If you do not want to provide self that function could be a static function (which means it does not rely on the type of the object itself). Then, you need to clarify that function, by #staticmethod decorator.
You missed self parameter on cantBeStudent method and when call it from contructor, it should be self.canBeStudent. Like this:
class Student:
def cantBeStudent(self):
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent()
The purpose of self in Python: What is the purpose of the word 'self', in Python?
Add self on the function cantBeStudent
class Student:
def cantBeStudent(self):
print("You don't classify as a stududent")
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent()
That happens because you need to specify that the function is contained in the same class of 'self'
If you would done in this way it would had worked:
def cantBeStudent():
print("You don't classify as a stududent")
class Student:
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
cantBeStudent()
The main difference is that in the first case, the function is inside the class Student, in the other case the function is out of the class, so don't need the 'self'

Trouble understanding classes in python

I am a beginner in python and I am having trouble understanding classes. I have a task that requires me to create a class that should return a students information e.g. name, id, age and marks. I have made a class but am having trouble with the output and keep on getting an attribute error:
print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)
AttributeError: 'Student' object has no attribute 'age'
I was wondering if someone could explain what I am doing wrong here as I am quite lost.
Rest of the code:
import random
class Student:
def __init__(self, name, ID):
self.name = name
self.ID = ID
def setAge(self, age):
self.age = age
self.age = random.randint(0, 100)
def setMarks(self, marks):
self.marks = marks
self.marks = random.randint(0, 100)
def Display(self):
print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)
student = Student("John", "ID123")
student.Display()
You didn't call student.setMarks() and student.setAge() , so marks and age attributes are not created in object yet.
The solution is to call these two methods before calling student.Display()
Python objects are a container that has attributes you can set. If you don't set an attribute but try to read it, you get an AttributeError, meaning that the attribute you are looking for does not exist.
Currently, student = Student(...) calls Student.__init__, which assigns the name and ID attribute of the object. You never call student.setAge or student.setMarks, so your object's age and marks attributes are never set and can not be accessed.
It is traditional to assign default values in the __init__ method if you want to generally avoid unexpected crashes like that.
Another thing is that rather having getter and setter methods, as Java would, for example, Python encourages the use of properties. Properties are objects in the class template that can be accessed like a normal attribute, but allow you to run arbitrary code in place of the access and assignment operators.
Putting all that together, you could write something like
class Student:
def __init__(self, name, ID, age=None, marks=None):
self.name = name
self.ID = ID
self.age = random.randint(0, 100) if age is None else age
self.marks = random.randint(0, 100) if marks is None else marks
#property
def marks(self):
return self._marks
#marks.setter
def marks(self, value):
# Example of a check you could do
if not isinstance(value, int):
raise TypeError('Marks must be an integer')
In your example, you try to access the variable, before the assignment. You would actually have to call student.setAge and student.setMarks with arguments.
On the other note, in your function setAge you instantly overwrite the value, so consider removing either first or second assignment:
def setAge(self, age):
self.age = age # first assignment
self.age = random.randint(0, 100) # second assignment
The age attribute is not set because it was never assigned:
student = Student("John", "ID123")
student.setAge(30) # John is 30 years old.
student.Display()
def __init__() works like a constructor. Try to include age attribute inside the __init__() function, call setAge() from __init__(), or setAge() explicitly before calling Display().
Good luck :)
A simpler version for your code:
import random
class Student:
def __init__(self, name, ID):
self.name = name
self.ID = ID
self.age = random.randint(0, 100)
self.marks = random.randint(0, 100)
def Display(self):
print("Student: " + str(self.name) + " ID: " + str(self.ID) + " Age: " + str(self.age) + " Mark: " + str(self.marks))
student = Student("John", "ID123")
student.Display()

What is the order of namespaces in inheritance?

A derived class has access to its base class member functions implicitly, unless I am mistaken. A derived class can also access its base class' attributes by prefixing a call to them like this: BaseClass.base_attribute. But I seemingly do not understand how instances of a derived class can use the methods of the base class. Example:
class Visitor():
""" Interface to Visitor
provide an interface to visitors that
perform an operation on a data collection """
def visitProduce():
pass
def visitMeat():
pass
def visitBakedGoods():
pass
def visitDairy():
pass
def visitNonFood():
pass
class PriceVisitor(Visitor):
__cost = 0.0 # total cost of groceries
def __init__(self):
self.__cost = 0.0
def visitProduce(self, p):
self.__cost += p.price()
def visitMeat(self, m):
self.__cost += m.price()
def visitBakedGoods(self, b):
self.__cost += b.price()
def visitDairy(self, d):
self.__cost += d.price()
def visitNonFood(self, nf):
self.__cost += nf.price()
class Groceries():
shopping_cart = [] # list of grocery items
def Groceries(self):
self.shopping_cart = []
def addProduce(self, p):
pass
def addMeat(self, m, lb):
pass
def addBakedGoods(self, b):
pass
def addDairy(self, d):
pass
def addNonFood(self, nf):
pass
def accept(self, v):
pass
def getShoppingCart(self):
print(self.shopping_cart)
def calculateCost(self, v):
for item in self.shopping_cart:
item.accept(v)
item.details()
print('Total cost is: $', v.__cost)
class Produce(Groceries):
def addProduce(self):
Groceries.shopping_cart.append(self)
def accept(self, v):
v.visitProduce(self)
def price(self):
return self.__price
def details(self):
print(self.__name, ' for: $', self.__price + '')
class Apples(Produce):
__name = None
__price = 3.25
def __init__(self, name):
self.__name = name
And here is a test of the Apple, Produce, Groceries, and PriceVisitor classes
import VisitorPattern as vp
def main():
# Visitor object
my_visitor = vp.PriceVisitor()
# Grocery object stores objects in its shopping_cart attribute
my_groceries = vp.Groceries()
# Add items
red_apple = vp.Apples('red apple')
gold_apple = vp.Apples('gold apple')
red_apple.addProduce()
gold_apple.addProduce()
my_groceries.getShoppingCart()
my_groceries.calculateCost(my_visitor)
if __name__ == '__main__':
main()
Now, the way I understand it is that upon the construction of the instance of Apple, it has access to Produce's method price(). Calling this method with an instance of the Apple class will then pass its own instance in place of the 'self'. The program then returns the value of the __price attribute belonging to the instance calling the method, in this case Apple. However, I get this error:
C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4>python test.
py
[<VisitorPattern.Apples object at 0x026E0830>, <VisitorPattern.Apples object at
0x026E0910>]
Traceback (most recent call last):
File "test.py", line 23, in <module>
main()
File "test.py", line 20, in main
my_groceries.calculateCost(my_visitor)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 60, in calculateCost
item.accept(v)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 71, in accept
v.visitProduce(self)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 28, in visitProduce
self.__cost += p.price()
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 74, in price
return self.__price
AttributeError: 'Apples' object has no attribute '_Produce__price'
How does the binding and namespaces actually work in inheritance? I could just write the price() method in each of Produce's derived classes, but that would defeat the point of inheritance. I think my problem also stems from name mangling, but still don't know what happens if I don't make my attributes 'private'. Clarification would be great. Thanks
Edit
I declared the constructor of Groceries wrong:
# Wrong way
def Groceries(self):
self.shopping_cart = []
# Should be
def __init__(self):
self.__shopping_cart = []
The product of a full time job and homework in the evening
What is the order of namespaces in inheritance?
Python uses the Method Resolution Order to find the method bound to that instance of the object.
It also invokes name mangling, which is why you can't find the method, _Produce__price. You're trying to use .__price but when it is inherited, Python adds the name of the class to the front of the name. Don't use two underscores, change the two underscores to one, and your code will work as you expect, and you'll consistently look up ._price which won't invoke the name mangling.
See the docs for more on this:
https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
Not really a direct answer to all your questions but I hope the following code sheds some light on how to do inheritance in Python.
class Produce(object):
def __init__(self, name=None, price=None):
self.__name = name
self.__price = price
def __str__(self):
return self.__name
#property
def bulk_price(self):
return self.__price * 100
class Apple(Produce):
def __init__(self, name="Apple"):
self.__name = name
self.__price = 3.25
super(self.__class__, self).__init__(self.__name, self.__price)
a = Apple("Gold Apple")
print a
print a.bulk_price
# Gold Apple
# 325.0
As you can see, I made name and price inaccessible in both classes. This way, I cannot just call them explicitly, i.e. a.__price. By using super as well in the child class, I am able to avoid referring to the base class further while still having access to its methods.
I have saw your error, your parent need to call child's function, but you have not transferred child to parent, so it will get the errors.Now I give my example:
class A:
def __init__(self, handler):
self.a = 5
self.real_handler = handler
def get(self):
print "value a = %d"%self.a
self.real_handler.put()
class B(A):
def __init__(self):
A.__init__(self, self) ##transport B to A
self.b = 3
def get(self):
print "value b is %d"%self.b
A.get(self)
def put(self):
self.b = 6
print "value b change into %d"%self.b
if __name__=="__main__":
b = B()
b.get()
In parent B, it will call the child A's fuction put(). I hope this can help you.

Object with __init__ in python

I have written some python code:
class key(object):
def __init__(self,name):
object.__init__(self,age)
this.name = name
this.age = age
def somefunction(self):
print "yay the name is %d" % self.name
baby = key('radan',20)
baby.somefunction()
When I create an instance of key with baby = key('radan',20), I got a TypeError. I don't know why I am getting this error. Is it because of object.__init__(self,age)?
If yes, please help me in explaining why we use object.__init__(self,age) and what the purpose of that is and help me solve this code.
Some pointers:
class Key(object): # in Python, classes are usually named with a starting capital
def __init__(self, name, age): # name all your arguments beside self
self.name = name # use self.name, not this.name
self.age = age
def somefunction(self):
print "yay the name is %s" % self.name
baby = Key('radan',20)
baby.somefunction()
# output: yay the name is radan
Actually, you can can name the self instance parameter whatever you like in Python (even this), but it makes the code harder to read for other people, so just use self.
You don't have to use object.__init__(self, name, age) here. If you remove that line and implement the changes above, your code will work just fine.
Your code contains several errors:
class key(object):
def __init__(self, name, age): # where's your age?
self.name = name # no need to call object.__init__
self.age = age # there is no such thing called "this", use self
def somefunction(self):
print "yay the name is %s" % self.name # use %s as the comment says
baby = key('radan', 20)
baby.somefunction()
output:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan
When you do baby = key('radar', 20) you are actually passing three arguments: the instance, the name and the age. However your initialiser is defined to take exactly two arguments so you get a TypeError.
self is the argument implicitly passed when referring to an instance of an object.
For your __init__ function, I would just do:
def __init__(self, name, age):
self.name = name
self.age = age
So we can assign the arguments passed as attributes to the current instance, conventionally called self.
It makes no sense here to call object.__init__ at all, just remove that line.
Apart from that, everything works fine (except use %s instead of %d).
Testing:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan

Categories

Resources