Why did my output change when I called the class attribute from an instance instead of the class? - python

(I'm fairly new to programming, so understand that my query might not make sense. I've tried my best to explain it but if you are still confused, you can ask me to clarify)
I understand the that we can call the class attribute number_of_people and increment by one so that when we create a new instance (in this context, a person), the number of people increases by one:
class Person:
# Class attribute
number_of_people = 0
# Constructor
def __init__(self, name):
self.name = name
# Every time we call the constructor, we increment the number of people.
Person.number_of_people += 1
# Driver code
p1 = Person("Jack")
print(Person.number_of_people)
# output gives 1
However, I'm having trouble with understanding the output of the code when we change the we choose to increment p1.number_of_people instead of Person.numer_of_people:
class Person:
# Class attribute
number_of_people = 0
# Constructor
def __init__(self, name):
self.name = name
# Every time we call the constructor, we increment the number of people.
p1.number_of_people += 1
# Driver code
p1 = Person("Jack")
print(p1.number_of_people)
# output gives 0 (I expected the output to be 1)
I thought that since class attributes can be accessed by both the class and instances, my change wouldn't make a difference. I suspect it has to do with p1 being mentioned in the class before it's mentioned in the driver code, but I'm not sure where to go from there.

I suspect the demonstration you were trying to do for yourself is to examine the difference between using the class attribute as a class attribute, vs as an instance attribute. Consider this difference:
class Person:
population = 0
def __init__(self,name):
self.name = name
Person.population += 1
p1 = Person("Jack")
p2 = Person("Tom")
p3 = Person("Bill")
print(p1.population)
print(p2.population)
print(p3.population)
Output:
3
3
3
Compared to:
class Person:
population = 0
def __init__(self,name):
self.name = name
self.population += 1
p1 = Person("Jack")
p2 = Person("Tom")
p3 = Person("Bill")
print(p1.population)
print(p2.population)
print(p3.population)
Output:
1
1
1
In the second example, the instance starts out using the class attribute, but as soon we change it, it creates an instance attribute that has a separate life.

Related

increment of a class attribute everytime an object is created

I would like to know how to increase/increment an attribute of a class.
I have a class called Account which has an attribute: self.code which is initialized at 0 and I want each time that an object is created the attribute increases.
so, for example:
account1=Account()
account2=Account()
account3=Account()
if I do account3.code I want to have 3 as a result.
Is there a possible way to do so? thank you in advance.
You can define a static variable for the Account object and increment it everytime you create a new object like this:
class Account:
code = 0
id = 0
def __init__(self):
self.id = Account.code
Account.code += 1
a = Account()
print(a.id) # 0
b = Account()
print(b.id) # 1
c = Account()
print(c.id) # 2
The code variable defines how many instances were created. Every account has an id
you can do it like this:
class Example:
code = 0
def __new__(cls, *args, **kwargs):
cls.code += 1
return super().__new__(Example, *args, **kwargs)
ex1 = Example()
print(ex1.code) # 1
ex2 = Example()
print(ex2.code) # 2
ex3 = Example()
print(ex3.code) # 3
using __new__ is more general than __init__ in the other answers since you can create an object without calling __init__.
If you also want each Account instance to store the code at which it was created you can use it like this
class Account:
last_code = 0
def __init__(self) -> None:
self.code = Account.last_code + 1
Account.last_code = self.code
a = Account()
b = Account()
c = Account()
print(a.code) # 1
print(b.code) # 2
print(c.code) # 3
Try to use a class attribute. Example:
class Account:
number_of_accounts = 0 # class attribute, not initialised for every instance
def __init__(self):
# whatever init code
Account.number_of_accounts += 1
account_1 = Account()
account_2 = Account()
account_3 = Account()
print(Account.number_of_accounts)
What about a sequence stored as static class variable.
Then with creation of each instance, the next value is drawn from that sequence.
import itertools
class Account:
id_iter = itertools.count() # sequence counter (usually used for IDs)
def __init__(self):
self.code = next(Account.id_iter)
See also:
How do you create an incremental ID in a Python Class

Python OOP: Automatically changing instances of a class after changing a class attribute?

class Employee:
pay_raise_percent = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
p1 = Employee('John', 50_000)
p2 = Employee('Alex', 75_000)
p3 = Employee('Caleb', 90_000)
Employee.pay_raise_percent = 1.04
print(p1.salary, p2.salary, p3.salary, sep='\n')
# 52000 78000 93600
is it possible to make it so that changing a class attribute leads to an automatic increase in the salary of all instances by this value without doing that explicitly for each of them?
Sounds like an excellent use case for properties. Properties look like ordinary instance variables but act like methods. Consider
class Employee:
pay_raise_percent = 1.00
def __init__(self, name, salary):
self.name = name
self._salary = salary # "Private" variable
#property
def salary(self):
return self._salary * Employee.pay_raise_percent
p1 = Employee('John', 50_000)
print(p1.salary) # 50000
Employee.pay_raise_percent = 1.04
print(p1.salary) # 52000
In reality, every access to p1.salary is calling a method that does some math on the real field p1._salary, so any updates to Employee.pay_raise_percent will be seen whenever the salary is requested.

How to count total number of person in a class

class Person:
number_of_people = 0 #class atribute,define for the entire class
def __init__(self,name):
self.name=name
p1 = Person('tim') # adding of object to class
p2 = Person('jill')
p3 = Person('Bill')
for x in range(Person.number_of_people): #will loop 3 time this case
print(Person.name) # how do i print all the names in a class
i cant seem to get this working
You would need a global variable to keep track of each instance this way, but more likely you would define a parent class, like People, and then have your Person class inherit from People. People would instead be in charge of tracking how many Person instances you've created. The latter option would be best if you have to look at more than just this one relationship between your Person instances. If you wanted to iterate through your Persons, for example, that could be a methods of your People class.
You could initialize a variable NUMBER_OF_PEOPLE=0 outside your class declaration and then each time someone call __init__ you can add one:
class Person:
# Declare class variables
NUMBER_OF_PEOPLE = 0
LIST_OF_PEOPLE = []
def __init__(self, name):
self.name = name
# Change the class variables
Person.LIST_OF_PEOPLE.append(self)
Person.NUMBER_OF_PEOPLE += 1
#classmethod
def get_number_of_people(cls):
return Person.NUMBER_OF_PEOPLE
p1 = Person("tim")
p2 = Person("jill")
people = [p1, p2] # create a list o two people
# Iterate through the list
for p in Person.LIST_OF_PEOPLE:
# Access the name by calling p.name
print("Name of the current person is: {}.".format(p.name))
# You can still access the NUMBER_OF_PEOPLE variable either by calling Person.get_number_of_people() (since it is a class method)
# or by simply using the NUMBER_OF_PEOPLE variable.
print("Currently, there are {} number of people in the class!".format(Person.get_number_of_people()))
# This will give 1 person.
Your issue here is that you don't completely understand object-oriented programming. You will get better by practising.

Python 2.7, defining a base class with attributes, id with init constructor

I am trying to define a generic base class Geometry, with a unique id for each object starting at 0. I am using init as the method.
I am trying to create a generic base class named Geometry that I will use to organize geometry objects like point or polygon and containing an id attribute starting at 0. I know all of the objects should have a unique ID. I'm using the constructor (__init__) when creating a new Geometry object (integer). And would like for the base class to automatically assign the ID of the Geometry object.
Current code:
class Geometry(object):
def__init__(self,id):
self.id = id
I think I am on the right path but I am not positive. Should I have id = 0 above def__init__(self,id)?
Any guidance will be appreciated.
If the first line of your class is id = 0 then it becomes a class attribute and is shared by all instances of Geometry and all of its children.
Here is an example of using a class scoped variable:
#!/usr/bin/env python2
class Geometry(object):
# ident is a class scoped variable, better known as Geometry.ident
ident = 0
def __init__(self):
self.ident = Geometry.ident
Geometry.ident += 1
class Circle(Geometry):
def __init__(self, radius):
Geometry.__init__(self)
self.radius = radius
def __str__(self):
return '<Circle ident={}, {}>'.format(self.ident, self.radius)
class Equilateral(Geometry):
def __init__(self, sides, length):
# super is another way to call Geometry.__init__() without
# needing the name of the parent class
super(Equilateral, self).__init__()
self.sides = sides
self.length = length
def __str__(self):
return '<Equilateral ident={}, {}, {}>'.format(self.ident,
self.sides, self.length)
# test that ident gets incremented between calls to Geometry.__init__()
c = Circle(12)
e = Equilateral(3, 8)
f = Circle(11)
print c
assert c.ident == 0
print e
assert e.ident == 1
print f
assert f.ident == 2
Something feels wrong about this, though I've not put my finger on it.
class Geometry(object):
def __init__(self,id=0):
self.id = id
__init__ in python is invoked when you create an instance of that class
circle = Geometry(1)

Creating an object with a reference to the object that created it

I have a program where an object creates another object. However, the second object that gets created needs to be able to access the first. Is this possible?
EG (pseudocode)
class parentObject():
parentVar = 1
# Create Child
x = childObject()
class childObject():
#Assign Var to the Var of the childs parent
childVar = parent.parentVar
>>> x.childVar = 1
is there a straitforward way to do this?
UPDATE:
I don't want to inheret the class, I need to be able to access the actual object that created it, as each object created from that class has different values.
Why not inherit the class?
class parentObject():
parentVar = 1
class childObject(parentObject):
childVar = parentObject.parentVar
>>> x = childObject()
>>> print(x.childVar)
1
If you are going to have different instances of the class, you should do it as this instead:
class parentObject(object):
def __init__(self):
self.parentVar = 1
class childObject(parentObject):
def __init__(self):
super(childObject, self).__init__()
self.childVar = self.parentVar
>>> x = childObject()
>>> print(x.childVar)
1
If you want a reference to the "parent" class, but inheritance is illogical, consider sending self in to the constructor:
class Room:
def __init__(self, name):
self.name = name
self.furniture = []
def add_chair(self):
self.furniture.append(Chair(self))
def __str__(self):
return '{} with {}'.format(self.name, self.furniture)
class Chair:
def __init__(self, room):
self.room = room
def __str__(self):
return 'Chair in {}'.format(self.room.name)
r = Room('Kitchen')
r.add_chair()
r.add_chair()
print r
print r.furniture[0]
Output:
Kitchen with [<__main__.Chair instance at 0x01F45F58>, <__main__.Chair instance at 0x01F45F80>]
Chair in Kitchen

Categories

Resources