python pass object or instantiate from within second object - python

I wondered if its better to:
Pass Object1 into Object2 for use
or
Instantiate Object1 from Object2
I could do either, but I am wondering what is better practice, or where i could get caught up.
These are rather rudimentary examples, and of course in the second example, i could just use the list. The actual code I am using, i need to use a lot of Object1 values to do things in Object2.
Example 1:
class Object1:
def __init__(self, person):
self.id = person[0]
self.name = person[1]
self.age = person[2]
class Object2:
def __init__(self, object1):
self.object1 = object1
retirement_age = self.object1.age + 25
print(self.object1.id)
print(self.object1.name)
print(retirement_age)
person = [1, 'Ryan', 30]
o = Object1(person)
r = Object2(o)
Example 2:
class Object1:
def __init__(self, person):
self.id = person[0]
self.name = person[1]
self.age = person[2]
class Object2:
def __init__(self, person):
self.o = Object1(person)
retirement_age = self.o.age + 25
print(self.o.id)
print(self.o.name)
print(retirement_age)
person = [1, 'Ryan', 30]
r = Object2(person)

Well, it depends, and only you can decide.
In the first example, you can use o in many places throughout your program without creating the same object over and over again (just make sure you are not modifying it anywhere, see below why).
The second option encapsulates the fact that Object1 even exists.
You need to ask yourself a few questions:
Will you use Object1 instances anywhere else in the code base? Does it even make sense to have a "standalone" Object1 instance outside of Object2 instance?
Does Object2 need to know about Object1? Does it need to know how to construct it? should it even care?
Can Object2 instances simply contain the attributes of Object1 directly? If the answer is 'yes' then Object1 is redundant.
You have to be very careful with option 1.
If the object you pass through to Object2 is modified anywhere, the changes will affect all of the corresponding Object2 instances.
class Object1:
def __init__(self, person):
self.id = person[0]
self.name = person[1]
self.age = person[2]
class Object2:
def __init__(self, object1):
self.object1 = object1
retirement_age = self.object1.age + 25
print(self.object1.id)
print(self.object1.name)
print(retirement_age)
person = [1, 'Ryan', 30]
o = Object1(person)
r = Object2(o)
# modifying o.name will affect r.object1.name as well
o.name = 'John'
print(r.object1.name)
# 1
# Ryan
# 55
# John

It depends on the context.
You could use the first method as Object2 doesn't need any information about how to construct an Object1.
On the other hand, if you needed to use person in Object2, you should use the second method as it would be easier to use person instead of self.object1.id, especially if you had to use person many times.

Related

I want to explicitly define a new class object. But how do I specify that I want a list of objects

Say I have a class of people
class person()
def __init__(self, fname:str, lname:str):
self.fname = fname
self.lname = lname
How do I say that I'm expecting a list of person?
class group()
def __init__(self, groupName:str, people:person|person[]):
self.groupName = groupName
self.people = people
Could I use ... people:person|list[person]): ?
A list of person objects is list[person] (or typing.List[person], prior to Python 3.9).
Yes, you can use people: list[person] to indicate that the people attribute is expected to be a list of person objects. The | symbol is not used in this case, because you only want to specify a single type, which is a list of person objects.
class group():
def __init__(self, groupName:str, people:list[person]):
self.groupName = groupName
self.people = people
When you use the | symbol, you are indicating that the attribute can store either one type or another. For example, person | list[person] would indicate that the attribute can store either a single person object or a list of person objects.
You can do
class Person:
def __init__(self, fname: str, lname: str):
self._fname = fname
self._lname = lname
class Group:
def __init__(self, name: str, people: Person|list[Person]):
self._name = name
self._people = people
and mypy will be happy. That said, it's probably a better design to simply have people: list[Person] (i.e. only a list is accepted), and have a 1-item list if only one person is in the group. Otherwise, you may have to implement different member functions for different types (or a bunch of if branches) if those functions use self._people.

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.

Unintuitive output given by classes in Python

So I've got this class:
class Student(object):
def __init__(self, studentID, name):
self.__studentID = studentID
self.__name = name
def set_studentID(self, value):
self.__studentID = value
def get_name(self):
return self.__name
and running this code:
x = Student
x.set_name(x, input("Name: "))
x.set_studentID(x, len(students))
students.append(copy.deepcopy(x))
x.set_name(x, input("Name: "))
x.set_studentID(x, len(students))
students.append(copy.deepcopy(x))
for i in (students):
print(i.get_name(i))
gives an unexpected output:
For the input:
a
b
the output is:
b
b
The expected output is:
a
b
If you answer please give me a short explanation of why it doesn't work
The reason your code isn't working is because you never instantiate your class, instead, you assign the class object itself to the name x
x = Student
When you really needed
x = Student()
Then you call the methods on the class object, whilst passing the class object itself as the first parameter, thus your getters and setters act on the class object.
Finally, classes are meant to be singletons, and the copy module special cases them. So if x is a class
copy.deepcopy(x) is x
Is always True, thus you never actually make a copy.
As a side note, your class definition looks like it was written by a Java developer using Python for the first time. The Pythonic way to do it is not to use getters and setters use properties, and only when you need to. Also, don't use double-underscores name-mangling unless you actually want that, which in this case, you dont.
The other answer explains why your code doesn't work as you expect it to. Here's how you could rewrite your code in a more pythonic way.
class Student(object):
def __init__(self, studentID, name):
self.studentID = studentID
self.name = name
students = []
name = input("Name: ")
students.append(Student(len(students), name))
name = input("Name: ")
students.append(Student(len(students), name))
for student in students:
print(student.name)
You don't need to write getter and setter methods unless you have to do some special processing.

Accessing a class instance's attributes using user input

So I have this code:
class vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
class car(vehicle):
def __init__(self):
vehicle.__init__(self)
self.type = 'car'
car1 = car()
print car1.make, car1.colour, car1.cost, car1.type, car1.age
n = raw_input()
dic = {'name' : n}
dic['name'] = car()
print dic['name'].make
In the last bit, I was able to resolve a previous issue I had: Creating an instance of the car class with its name being one that the user inputs in n
Now, say I wanna ask the user to input a name and now I have to find the class' instance that has that name.
For example if at one point an instance with the name car2 was created. Now user wants to get info about car2 and inputs 'car2'. how can I use this input to access attributes of the instance called car2?
I tried:
a = raw_input()
dic['fetch'] = a
dic['fetch'].make
does not work.
It seems to me you have a fair bit of misunderstanding. The way you're assigning the input into the dictionary doesn't make sense. Your description indicates that you want a dictionary that maps a "name" to a car description.
Your initial creation of the dictionary is off. The way you're currently doing it, you're actually losing the name the user inputs when you assign the car data. Some better variable naming might help you. Create your dictionary like this:
cars_by_name = dict()
name = raw_input()
cars_by_name[name] = car()
So now you have a name (given by the user) that maps to a car description.
Now you need to fetch that same car instance again. You do it by using the name as the key in the dictionary:
name2 = raw_input()
print cars_by_name[name2].make
Next, let's look at your classes. My first question: why do you need a vehicle and a car class? If you're never going to have classes other than car inheriting from vehicle, you don't really need them both. Even if you do plan the have more subclasses, I would probably still recommend against inheritance here. Your vehicle has no behavior (methods) for subclasses to inherit. It's just a data object. With duck typing so strongly encouraged in Python, inheriting from a class with no methods doesn't buy you anything. (What a base class would buy you with methods is that you'd only have to define the method in one place, making it easier to modify later on.) Particularly in your case, there doesn't seem to be any motivation to create a subclass at all. A single class for all vehicles will work just fine. So let's simplify your classes:
class Vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
self.type = 'car'
(Also, note that class names are usually given in camel case in Python.) Now there's one more problem here: those constants. Not all Vehicles are going to have those same values; in fact, most won't. So lets make them arguments to the initializer:
class Vehicle(object):
def __init__(self, age, make, colour, cost, type):
self.age = age
self.make = make
self.colour = colour
self.cost = cost
self.type = type
Then you create one like this:
v = Vehicle(6, 8, 'colour', 'cost', 'car')
Good luck in your endeavors learning. Hope this helps.
If I understand you correctly and you want to map string names to instances:
n = raw_input()
dic = {n: car()}
print dic[n].make
print(dic)
dic[n].cost = 10000
print(dic[n].cost)
Another option would be to take a name for each car instance and have a class attribute dict mapping names to self.
In [13]: paste
class vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
class car(vehicle):
dic = {}
def __init__(self, name):
vehicle.__init__(self)
car.dic = {name: self}
self.type = 'car'
self.name=name
car1 = car("car1")
car2 = car("car2")
car2.colour="blue"
print car1.make, car1.colour, car1.cost, car1.type, car1.age
n = raw_input()
print car.dic[n]
print car.dic[n].make
print car.dic[n].colour
## -- End pasted text --
8 colour cost car 6
car2
<__main__.car object at 0x7f823cd34490>
8
blue

Python: showing attributes assigned to a class object in the class code

One of my classes does a lot of aggregate calculating on a collection of objects, then assigns an attribute and value appropriate to the specific object: I.e.
class Team(object):
def __init__(self, name): # updated for typo in code, added self
self.name = name
class LeagueDetails(object):
def __init__(self): # added for clarity, corrected another typo
self.team_list = [Team('name'), ...]
self.calculate_league_standings() # added for clarity
def calculate_league_standings(self):
# calculate standings as a team_place_dict
for team in self.team_list:
team.place = team_place_dict[team.name] # a new team attribute
I know, as long as the calculate_league_standings has been run, every team has team.place. What I would like to be able to do is to scan the code for class Team(object) and read all the attributes, both created by class methods and also created by external methods which operate on class objects. I am getting a little sick of typing for p in dir(team): print p just to see what the attribute names are. I could define a bunch of blank attributes in the Team __init__. E.g.
class Team(object):
def __init__(self, name): # updated for typo in code, added self
self.name = name
self.place = None # dummy attribute, but recognizable when the code is scanned
It seems redundant to have calculate_league_standings return team._place and then add
#property
def place(self): return self._place
I know I could comment a list of attributes at the top class Team, which is the obvious solution, but I feel like there has to be a best practice here, something pythonic and elegant here.
If I half understand your question, you want to keep track of which attributes of an instance have been added after initialization. If this is the case, you could use something like this:
#! /usr/bin/python3.2
def trackable (cls):
cls._tracked = {}
oSetter = cls.__setattr__
def setter (self, k, v):
try: self.initialized
except: return oSetter (self, k, v)
try: self.k
except:
if not self in self.__class__._tracked:
self.__class__._tracked [self] = []
self.__class__._tracked [self].append (k)
return oSetter (self, k, v)
cls.__setattr__ = setter
oInit = cls.__init__
def init (self, *args, **kwargs):
o = oInit (self, *args, **kwargs)
self.initialized = 42
return o
cls.__init__ = init
oGetter = cls.__getattribute__
def getter (self, k):
if k == 'tracked': return self.__class__._tracked [self]
return oGetter (self, k)
cls.__getattribute__ = getter
return cls
#trackable
class Team:
def __init__ (self, name, region):
self.name = name
self.region = region
#set name and region during initialization
t = Team ('A', 'EU')
#set rank and ELO outside (hence trackable)
#in your "aggregate" functions
t.rank = 4 # a new team attribute
t.ELO = 14 # a new team attribute
#see witch attributes have been created after initialization
print (t.tracked)
If I did not understand the question, please do specify which part I got wrong.
Due to Python's dynamic nature, I don't believe there is a general answer to your question. An attribute of an instance can be set in many ways, including pure assignment, setattr(), and writes to __dict__ . Writing a tool to statically analyze Python code and correctly determine all possible attributes of an class by analyzing all these methods would be very difficult.
In your specific case, as the programmer you know that class Team will have a place attribute in many instances, so you can decide to be explicit and write its constructor like so:
class Team(object):
def __init__(name ,place=None):
self.name = name
self.place = place
I would say there is no need to define a property of a simple attribute, unless you wanted side effects or derivations to happen at read or write time.

Categories

Resources