Python understanding classes and functions - python-3.x

New to python and have been working on improving my skills overall, however, I struggle with understanding classes and functions.
Why can or can't I do the following code below
class Person():
name = 'Tom'
age = 31
has_job = False
Person.name = 'Tom'
Person.age = 31
Person.has_job = False
print(Person.name, Person.age, Person.has_job)
compared to this
class Person():
def __init__(self, name, age, has_job):
self.name = name
self.age = age
self.has_job = has_job
p1 = Person('Tom', 31, False)
Is this just bad practice or is it something else entirely?

I don't think that writing a class like your first example would be very usefull, because the attributes remain the same for each instance.
That means that every Person will be called by default 'Tom', will have the age: 41 and "has_job" will be set to false.
In the second example you've got a specific constructor that will initialise those variables and that's going to be more usefull. There's only one problem: you forgot to put ":" after def __init__(self, name, age, has_job) .
Also be aware of the indentation.
Your code should look like this:
class Person():
def __init__(self, name, age, has_job):
self.name = name
self.age = age
self.has_job = has_job
p1 = Person('Tom', 31, False)
print(p1.name);

Python is white space sensitive. Unless you want to change the default values in you class you do not need to redefine them.
class Person():
name = 'Tom'
age = 31
has_job = False
'''
change these will change the class values
Person.name = 'Tom'
Person.age = 31
Person.has_job = False
'''
print(Person.name, Person.age, Person.has_job)

In the first section of your code you are trying to define class attributes. These are attributes that do not change between instances of your class. On the other hand if you define variables in the def init(self) method these are parameters you must pass when creating the class and will be unique to each instance of the class you create. These are called instance attributes.
class Person():
# these are class attributes.
name = 'Tom'
age = 31
has_job = False
class Person2():
def __init__(self, name, age, has_job)
# these are instance attributes
self.name = name
self.age = age
self.has_job = has_job
In your first code snippet you did not indent the classes attributes appropriately when you created the class. Check my example above to see how that would be done.
So in your case since each person will be a new instance of your Person class, you do not want to have name, age and has_job as class attributes since those are unique to every person you create. If you had those variables as class attributes then each person you create using your Person() class will have the same name, age, and has_job values.
If you created a class with class attributes and then changed the class attributes of the class instance every time it would not be pythonic. Rather you should create instances of the class with instance attributes.
I HIGHLY recommend watching Corey Shafer OOP tutorials on youtube as they cover all this extensively: https://www.youtube.com/watch?v=ZDa-Z5JzLYM&list=PL-osiE80TeTt2d9bfVyTiXJA-UTHn6WwU&index=40

Related

Can I take 'outer' class Attributes and use them in 'inner' class?

Can I take 'outer' class Attributes and use them in 'inner' class ?
I want to make an outer class that contains the name and age of a man and the inner class contains the age of a man.... I tried this way but VSC told me that this is a syntax error
I tried to search for a way to inherit from 'father_city' class and I found this but I didn't understand it
class father_city:
def __init__(self, city):
self.city = city
self.name = 'mark'
def show_city(self):
print(f"hello {self.name} you live in {self.city}")
class father_age:
def __init__(self, age):
self.age = age
def show_age(self):
print(f'hello {father_city('Ny').name} your age is {self.age}')
ob = father_city('Ny')
print(ob.father_age(26).show_age())
your show_age method has a syntax error,when using ' inside a fstring you should use " to surround the string.
print(f"hello {father_city('Ny').name} your age is {self.age}")

How can i avoid repetitive calling of instance variables in Subclasses?

i was wondering if there is a way in Python to get rid of repetitive calling of instance variables , when creating subclasses.
for example:
class Name:
def __init__(self,first,last):
self.first = first
self.last = last
def __str__(self):
return f"Users first name is : {self.first}, Users last name is: {self.last}"
def __repr__(self):
return f"first:{self.first}, last:{self.last}"
class Cash(Name):
def __init__(self,first,last,cash):
super().__init__(first,last)
self.cash = cash
def full(self):
return f"{self.first},{self.last},{self.cash}"
c1 = Cash("Exa","Cool",200)
print(c1.full())
Is it possible to call all instance variables (self.first,self.last...) from "Name", without having to mention them in the constructor of "Cash"
something like:
class Cash(Name):
def __init__("all from Name" + new one ("cash" in this example)):
super().__init__("all from Name")
self.cash = cash
In your case, you can change the Cash class to look like this:
class Cash(Name):
def __init__(self,*inputs):
super(Cash,self).__init__(*inputs[:-1])
self.cash = inputs[-1]
def full(self):
return f"{self.first},{self.last},{self.cash}"
but for a more general solution that covers all situations take a look at this similar question.

Accessing a Single Variable from Another Class (Python)

So, I'm having trouble figuring out how to best structure my Python code. I have a short program using classes, which I've recreated in an example below:
prof1 = Professor()
prof2 = Professor()
professor_list = [prof1, prof2]
class Professor:
def __init__(self):
self.name = ""
self.department = ""
self.course = ""
self.students = []
class Student:
def __init__(self):
self.name = ""
self.activities = []
So basically, I have a list of objects which are instances of the Professor class. Each Professor object also contains a list of Student objects, each of which is an instance of the Student class. I want each Student to also be able to access the course variable from its corresponding Professor (the Professor object with that Student in its list), but I wasn't sure what the neatest way to do this would be.
I considered having Student inherit from Professor, but that seemed sloppy, since I don't actually want to inherit all of the variables and functionality from Professor. For example, Professor's department variable has nothing to do with a Student object, and I don't want Students to have a department attribute. Also, Student isn't a sub-type of Professor, which is what I would associate with inheritance.
I also thought of manually adding the course information to each Student as an attribute, but that didn't seem right either. In the structure I want, the course would be a unique attribute of the Professor, and the various students would be associated with the Professor. All the students that would need to access the course variable are in the Professor's student list - so shouldn't there be a neater way of allowing them to use it than adding it manually to each student?
Here's one more idea: I might include a reference to the corresponding Professor class in each Student, and access the variable that way. Would this be bad practice?
I'd really appreciate any help you can give me with this.
You could go the route of the reference to the Professor, but I think the easiest way would be to create an add_student method which adds the Student to the corresponding Professor's list, as well as marks the course on the Student, so you don't have to do this manually and you can avoid a circular reference (which also is unnecessary because if you're marking the Professor reference on the Student just to get the course attribute, then you may as well just mark the course attribute directly):
class Professor:
def __init__(self):
self.name = ""
self.department = ""
self.course = ""
self.students = []
def add_student(self, student):
self.students.append(student)
student.course = self.course
class Student:
def __init__(self):
self.name = ""
self.activities = []
self.course = None
Alternatively, you could add a method to the Professor which checks if the Student belongs to it and use that to get the course:
prof1 = Professor()
prof2 = Professor()
professor_list = [prof1, prof2]
class Professor:
def __init__(self):
self.name = ""
self.department = ""
self.course = ""
self.students = []
def teaches(self, student):
return student in self.students
class Student:
def __init__(self):
self.name = ""
self.activities = []
def get_course(self, professors):
for professor in professors:
if professor.teaches(self):
return professor.course
return None

How can I return a list of class attributes in Python?

I have created a class Students that has 14 attrs.
There are 20 members of the class that has 14 attrs. One of the attrs is self.project. This was initialized under:
def __init__(self, name, gender, test1, project, final, midterm.....etc):
self.name = name
self.gender = gender
self.test1 = test1
self.project = project
# .....etc and so on and so forth
all 20 members have a project...how can I return a list of the just project entries of all 20 members of my class?
Say, you have a list of instances of your class called members. You can iterate over it and extract the project attribute with a list comprehension:
projects = [mate.project for mate in members]
Also, to reduce the initialisation like self.project = project in __init__, you can use collections.namedtuple.
Then your class could look like this:
from collections import namedtuple
Member = namedtuple("Member", "name gender test1 project")
class Member(Member):
def sayHello(self):
# your own methods can use `self.project` and the like as usual
print(f'Hello, {self.name}!')

Trying to save instances in their class

My problem is that I'd like to save instances of a class in a class dict (here named catalog).
Each time I create a new instance, I want it to be stored in catalog, the keys being the self.id value, and the value being the instance itself.
I already looked for some solution with new, but it seems like new can only return an instance and dont initialize it, as init do the job.
def Mother():
id_m=0
catalog={}
def __init__(self):
self.value=0
self.id=None
self.sub_dict={}
self.id_attrib()
Mother.id_m+=1
def id_attrib(self):
if self.id==None:
self.id=id_m
else:
pass
def __sub__(self,sub):
if type(sub) is not Mother:
return self
else:
index=0
while index not in self.sub_dict.keys():
index+=1
self.sub_dict[index]=sub
So far, this code only initialize a new instance.
What I want to do further is to provide a class method that updates instances in self.sub_dict.
s1=Mother()
s2=Mother()
s1=s1-s2 ## adds s2 to the self.sub_dict
s2.value=150 ##How to update the value in self.sub_dict?
Thanks for your answers!
I'm not 100% sure what you are trying to do with sub, but let me know if this gets you closer. Add a comment if you need follow-up and I'll help any way I can;
from typing import Dict
class Mother():
all_mothers = dict() # type: Dict[str, Mother]
def __init__(self, last_name, first_name):
self.last_name = last_name # type: str
self.first_name = first_name # type: str
Mother.all_mothers[last_name] = self
jones = Mother("Jones", "Martha")
smith = Mother("Smith", "Sarah")
print(smith.first_name)
print(Mother.all_mothers['Smith'].first_name)
smith.first_name = "Jane"
print(smith.first_name)
print(Mother.all_mothers['Smith'].first_name)
Mother.all_mothers["Jones"].first_name = "Sue"
print(jones.first_name)
Sarah
Sarah
Jane
Jane
Sue

Resources