Difference between two attributes in a class - python

Can you please explain to me what is the difference between the attribute that we put in def __init__ (c_name) and the one we put inside the class (self.name)?
class human:
def __init__(self, c_name, c_age):
print("Creation of Human...")
self.name = c_name
self.age = c_age

c_name is a parameter of the function __init__ and __init__ is called whenever a new instance of that class is created. In Python self also represents an instance of the class. So here, doing self.name = c_name assigns the local value of c_name to the attribute self.name. This means it will be accessible for the life of the object instead of just inside the call to __init__

when we use attribute in __init__ function
the init function automatically get involved during the the object creation
anything we create using self. refers to the class attribute
according to the question self.name is attribute specific to the the object created from class human.
we can access this attribute from anywhere like this
a = human("anyname",11)
print (a.name)
output
anyname
when we use a function with some other name

First, c_name is not an attribute. It's called "parameter".
self.name is an instance variable. Because self is pointing to our recently created instance and we set the attribute name to it.
c_name is local to the __init__ method, so if we don't store it in our instance namespace, we can't have access to it inside another methods or elsewhere outside __init__. Just like regular functions:
def fn(a):
a += 1
fn(10)
print(a) # NameError: name 'a' is not defined

Related

How to change an instance variable in python using a method?

I'm very new to python and currently practicing OOP. One of the problems I'm facing is how to change an instance variable in python using a method.
My code:
class Monster:
def __init__ (self, name, health):
self.name = name
self.health = health
#classmethod
def change_name (self, given_name):
self.name = given_name
mon = Monster("Drake", 100)
print(mon.name)
mon.change_name("Derrick")
print(mon.name)
Output:
#Expected:
Drake
Derrick
#Actual:
Drake
Drake
Can someone tell me what the problem is and how I can solve it?
It happens because the change_name method is declared as a
classmethod
Which means its self parameter is not the object (instance of the class) but the class, in fact you should rename it to cls to make it clear.
Assigning tbe variable via the class creates it in the class' namespace, not in the object's.
But when looked up via the object, python looks for it in the object's namespace and if it cannot be found, it looks for it in the class' (and above in the superclasses' if not found yet)
Since you do not seem to need it , as the name attribute belongs to each monster instance, the solution is to make the method a plain instance one by removing the #classmethod decorator.

Access Class method and variable using self

In below example Test class has two instance method and one classmethod
In set_cls_var_1 method I set class variable using self.
In set_cls_var_2 method I call class method using self.
class Test():
#class variable
cls_var = 10
def __init__(self):
obj_var=20
def set_cls_var_1(self,val):
#second method to access class variable
print "first "
self.cls_var = val
def set_cls_var_2(self):
print "second"
self.task(200)
#classmethod
def task(cls,val):
cls.cls_var = val
t=Test()
#set class variable by first method
t.set_cls_var_1(100)
print Test.cls_var
#set class variable by second method
t.set_cls_var_2()
print Test.cls_var
Output
first
10
second
200
Expected Output
first
100
second
200
My question is:
why only classmethod can call by self, Why not class variable
When you attempt to access an object's attribute using self, Python first searches the object's attributes. If it cannot find it there, then is searches the object's class's attributes. That is what's happening in your case;
Python first searches t's attributes. It doesn't find cls_var, so it then searches the T class's attributes. It finds cls_var so it stops, and returns cls_var's value.
However, when assigning attributes to self, Python always assigns them directly to the object, and never the object's class unless explicitly told to do so. That's why assinging self.cls_var to 100 didn't affect Test's cls_var attrbiute.
I find something else that always use following way to access classmethod or variable in instance method
class Test():
#class variable
cls_var = 10
def __init__(self):
obj_var=20
def set_cls_var_1(self,val):
#first method to access class variable
print "first type"
cls = self.__class__
cls.cls_var = val
t=Test()
#set class variable by first method
t.set_cls_var_1(100)
print Test.cls_var
When defining the Test class like you did, python creates a class object called Test which has an attribute cls_var equal to 10. When you instantiate this class, the created object doesn't have cls_var attribute. When calling self.cls_var it is actually the class' attribute that is retrieved due to the way python resolves attributes.
However when set self.cls_var the value is set at the object level! So further call to self.cls_var will give you the value of the object's attribute, and not the class' anymore!
Maybe this bit of code will make this clearer:
class A(object):
a = 1
a = A()
print a.a # prints 1
A.a = 2
print a.a # prints 2
You see that even though when set the value at the class level, the changes are repercuted on the object, because, python will look up for the attribute in the class when it is not found at the object level.
When calling Test.cls_var it is the cls_var attribute of the class you are accessing! Not the one of the object you just modified.

Why can't I call a method from my Python class?

I am learning Python and currently working with classes. I am trying to make a basic game to help learn it and am having a weird issue with calling methods
from it. I have the main.py file which creates an instance from the class in the Character.py file.
This is the Character.py file:
class Character:
name=""
def __init__(Name):
name=Name
def getName():
return name
This is the main.py file:
from Character import *
player = Character("James")
print(player.getName())
I am not sure what the issue is. This is the error I get:
Traceback (most recent call last):
File "C:\Users\dstei\Documents\Python\It 102\Final Project\Main.py", line
12, in <module>
print(player.getName())
TypeError: getName() takes 0 positional arguments but 1 was given
It is saying I am giving 1 positional argument but I don't see where I gave any. What am I missing?
Since you have a class with instance methods, you need to include the first argument (self by convention) to refer to the current instance. Also, make sure to set the variable as an instance variable by using self, the current instance:
class Character:
def __init__(self, Name): #self is the current instance
self.name=Name #set the variable on the instance so that every instance of Character has a name
def getName(self):
return self.name #refer to the name with the instance
Python internally passes the new instance of a class as the first argument to all the class methods, like this in languages such as Java. The error comes from the fact that Python passes the instance as the first argument internally but your getter is not defined to take an argument.
With the above code, when you call the method upon an instance, the instance is internally passed as the first argument and Python doesn't complain as you specify that it takes an argument, self, and name is set correctly on the instance.
Note: By convention, Python does not use camelCase, but underscores, so your getter should by convention look like this:
def get_name(self):
#...
Also see chepner's answer which explains why getters and setters aren't usually needed. Just get and modify the instance variable by using dot notation:
print(player.name) #get
player.name = "Jeff" #set
As others have mentioned, even instance method must be declared with an extra argument, typically named self (although that is a conventional, not a required, name).
class Character:
def __init__(self, name):
self.name = name
def get_name(self):
return name
However, Python does not have any kind of enforced visibility (such as public or private), so such trivial getters and setters aren't usually written. Documentation about which attributes you are "allowed" to modify are considered sufficient protection.
class Character:
def __init__(self, name):
self.name = name
c = Character("Bob")
print(c.name) # instead of c.get_name()
c.name = "Charlie" # instead of c.set_name("Charlie")
You are forgetting to add the parameter self. self is an object reference to the object itself, therefore, they are same. Python methods are not called in the context of the object itself. self in Python may be used to deal with custom object models or
class Character:
def __init__(self,name):
self.name=name
def getName(self):
return self.name
To see why this parameter is needed, there are so good answers here:
What is the purpose of self?

Python Creating Classes Code

Sorry for the poor title, didnt know what to put.
When creating a class in python i use this,
class NewClass(object):
def __init__(self,name):
self.name = name
but why is it different to this?
class NewClass(object):
def __init__(self,name):
name = self.name
Surely the equals sign means it is the same process? Why is different?
= is an assignment statement, you appear to be confusing this with a ==, the equality comparison operator.
The statements are entirely different:
self.name = name
assigns the value referenced by the local variable name to the attribute name on the object referenced by self. It sets an attribute on the newly created instance, from the value passed into the initialiser method.
The alternative statement
name = self.name
assigns the value of the attribute name found on self, to the local variable name. It rebinds the local name (replaces the old value with a new). Once the method ends, the effects are gone. You are likely to get an AttributeError as the attribute name doesn't exist on self at that point in time.
If == had been used, then usually yes, name == self.name is the equivalent of self.name == name. However, objects can override how equality is tested by defining a new implementation for the __eq__ method so the two expressions could theoretically produce different results.
class NewClass(object):
def __init__(self,name):
self.name = name
This assigns the instance variable self.name to the value of the argument which was provided to __init__() (in this case name)
class NewClass(object):
def __init__(self,name):
name = self.name
This assigns the instance variable self.name to the value of the argument name. This would throw an error if self.name was not assigned to something else earlier in the __init__() function.

Python property not working

I have an object which inherits from ndb.Model (a Google App Engine thing). This object has a property called commentid:
class Comment(ndb.Model):
commentid = ndb.StringProperty()
Reading a bunch of articles, they all say this is the way to implement a property:
#property
def commentid(self):
if not self._commentid:
self._commentid = "1"
return self._commentid
but I get an error saying Comment object has no attribute _commentid. What am I doing wrong?
Edit: Ok obviously I'm a bit confused here. I come from Objective-C, where if you have a property called x then you automatically get a variable called _x in your getters and setters. So I thought this is what was happening here in Python too. But apparently I need to manually set a value for the variable with an underscore prefix.
All I want is to implement a getter where I do some checking of the value before returning it. How would I do this?
Implementing a property like that requires you to define the attribute for your object. What you're doing there, is defining a class called Comment but you don't define any attributes for it's objects, you define them for the class itself.
Let me demonstrate with a small example:
class ExampleClass:
name = "Example Object"
a = ExampleClass() # Init new instance of ExampleClass
print(a.name) # a doesn't own an attribute called "name"
print(ExampleClass.name) # --> "Example Object"
In the above example, I define class ExampleClass and give it a variable name with a value Example Object. After that, I create an object a = ExampleClass(), however it does not get the name attribute, cause the attribute is defined for the class itself, not for it's objects.
To fix this problem, you define the name inside __init__ -method, which gets called whenever an object of that class is created.
class ExampleClass:
def __init__(self):
self.name = "Example Class"
a = ExampleClass() # Init new instance of ExampleClass
print(a.name) # --> "Example Class"
print(ExampleClass.name) # --> ERROR: Exampleclass.name doesn't exist
There I define the ExampleClass again, but I also define __init__ method for it. Init method takes only one parameter, self, which will be automatically given to the function. It's the object which is being created. Then I set self.name = "Example Class", and since self is the object itself, we set the object's attribute name.
Creating the property
To implement setter and getter for your attribute, you add the following:
class ExampleClass:
def __init__(self):
self.name = "Example Class"
#property
def name(self):
if not self._name:
pass #blabla code here
return self._name
#name.setter
def name(self, value):
#blabla more code
self._name = value
Also, you should edit the __init__ method to take name as a parameter too.
def __init__(self, name="Example Object"):
self.name = name
If you access self._commentid directly, it needs to be defined or it'll raise an exception. Since you're instead checking if _commentid is defined at all (to give it a default value), I'd use hasattr:
#property
def commentid(self):
if not hasattr(self, "_commentid"):
self._commentid = "1"
return self._commentid

Categories