How to edit a instance variable? - python

I'm new to python and as I was doing an assignment for class, I got stuck using init method.
class Customer(object):
def __init__(self, number, name):
self.name = name
self.number = number
self.orders = []
def addorder(self, order):
self.orders.extend(order)
return self.orders
def __str__(self):
return str(self.orders)
Customer('308','John').addorder((1,2,3,4))
print(Customer('308','John'))
The output is an empty list [].
I want the output to be [1,2,3,4]
What am I doing wrong here?

The issue is that you have two Customer objects. I.e. your print line:
print(Customer('308','John'))
Is creating a new Customer object with a number of '308' and a name of 'John'. It's completely unrelated to the customer on the previous line.
To fix this, you should assign your first object to a variable (think of it like a handle, that lets you access the object), and then print that:
john = Customer('308','John')
john.addorder((1,2,3,4))
print(john)

You're creating two instances of the class
class Customer(object):
def __init__(self, number, name):
self.name = name
self.number = number
self.orders = []
def addorder(self, order):
self.orders.extend(order)
return self.orders
def __str__(self):
return str(self.orders)
customer = Customer('308','John')
customer.addorder((1,2,3,4))
print(customer)

Keep in mind that each time you "call" a class, you instantiate a new object (this is why in many languages other than Python, this actually requires the keyword new). So, in your example, you're instantiating two different objects (that don't share their properties). Instead, you should save them in a variable:
customer = Customer("308", "John")
customer.addorder((1, 2, 3, 4))
print(customer)

Related

How to create an object with function created outside class in Python?

I have just started learning OOP in python and I have learned basics like creating class and it's methods, variables and Constructors. Now to create an object we use following steps.
class Example: #Class
name = None
number = None
def __init__(self, name, number): #Constructor
self.name = name
self.number = number
#Step 1
harry = Example("Harry", 45) #Creates an Object
Now here we have manually created Object of Example class named harry.
I have a question that how to create an object with a function.
Like we created a function outside the class and we passed arguments to like name and number and when that function is called it will create a Object of class.
Are you looking for something like this ?
def build_object(name, number):
# returns Example object initialized with name and number
return Example(name, number)
You're talking about Factory Methods
To create objects in functions and returning it would be like:
class Example:
def __init__(self, name, number):
self.name = name
self.number = number
def object_creator(name, number):
new_obj = Example(name, number)
return new_obj
if __name__ == "__main__":
example_object = object_creator("Iago", 1)
print(example_object.name)
print(example_object.number)
Not sure what do you mean exactly.
in oop you can create a function/method then you can from it's object or class.
class Example: #Class
name = None
number = None
def __init__(self, name, number): #Constructor
self.name = name
self.number = number
def your_function(self, x, y):
return x + y
#Step 1
harry = Example("Harry", 45) #Creates an Object
result = harry.your_function(5, 2)
print (result)
output : 7

Python object inside other objects are not isolated

I was expecting the object items inside other python objects are isolated.
However, the following code shows my expected result is wrong. It seems python uses a central item_list for all Group items. How can I fix this?
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = []
def __init__(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Result
len = 4
bb2 = bb1
Version
python3 --version
Python 3.5.2
Well, first of all we should make a difference between class attributes and instance attributes. A class attribute (like item_list) belongs to the class itself (in this case "Group"), so it will be accessible by calling Group.item_list. On the other hand, you can define a item_list for every instance of Group by defining self.item_list = [] inside the Group class constructor (__init__).
The Group.item_list array will be unique for the whole class, and thus will be suitable to store things that you want to share across instances (such as g2).
The self.item_list array (that will be different for each instance of Group) will hold values exclusively for the instance itself, so each Group will have its own item_list.
I think you are aiming for the second approach (instance variables) so you should move the definition of item_list = [] inside the class constructor.
The result should look like this:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
item1 = Item("itemName")
group1 = Group(item1)
# This should print an array containing the *item1* instance
print(group1.item_list)
print(group1.item_list[0] == item1)
Variables that are declared outside of the __init__ method (in this case item_list) are shared between all instances of a class (called class variables), which is why your expected result is wrong.
On the other hand, variables inside the __init__ only belong to the given instance of that class.
Your using class variables, which are similar C++ static variables inside classes (i.e. that variable is shared by ALL class instances). You need to put it inside the __init__ (constructor) to make it so each class creates its own version:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
# Though typically you would also have a function like this:
def add_item(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Instance vs class attributes is covered in other answers. I want to add that you can avoid having shared instance variables by using an immutable type (e.g. tuple) instead of a mutable type (e.g. list) for class attributes. Like that they won't be shared among instances while still allowing you to define class attributes.
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = tuple()
def __init__(self, item):
self.item_list += (item,)

Passed an object instead of object's property?

There is two classes - Company and Project. Company object has property projects as list, that should indicate list of Project instances that is added to the company
Here is realization of classes and methods to add projects to the company:
class Company(object):
def __init__(self, companyname):
self.companyname = companyname
self.projects = list()
def show_projects(self):
print(f"Company projects: {self.projects}")
def add_project(self, name):
return self.projects.append(Project(name))
class Project(object):
def __init__(self, name):
self.name = name
But when I try to initialize company, then project and add it to company, add add_project returns not project.name, but object itself, so i.e. output of this:
firm = Company("New")
first = Project("first")
print(first.name)
firm.add_project(first.name)
firm.show_projects()
will be:
first
Company projects: [<__main__.Project object at 0x00A54170>]
Why is it passing not name, but object itself? Can't find out what is missing here.
firm.projects is a list so in show_projects when you print it it will be a list of objects. One solution would be to modify show_projects to format the list into a comma separated string first:
def show_projects(self):
formatted_projects = ','.join([p.name for p in self.projects])
print(f"Company projects: {formatted_projects}")

Using certain attributes of an object to make other objects in a different class

For a program that creates a timetable for a doctor(specialist) I want to use certain attributes of an object created by a different class to be used in the class that makes the timetable for the doctor.
class makePatient(object):
def __init__(self,name,room):
self.name = name
self.room = room
def getPatient(self):
print(self.name)
print(self.room)
class makeSpecialist(object):
def __init__(self,name,specialization,timetable):
self.name = name
self.specialization = specialization
self.timetable = timetable
def getSpecialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
class makeAgenda(object):
def addAgenda(self):
self.timetable.append()
#I want to append the name of the patient I have defined here.
print(self.timetable)
patient1 = makePatient("Michael","101")
specialist1 = makeSpecialist("Dr. John","Hematology",[])
What do I do now, to make sure that the name "Michael" gets added to the list [] of specialist Dr. John?
Thanks in advance, I will provide further details if necessary!
I think another approach would be better; you can put the whole makePatient object into the timetable for the specialist:
specialist1 = makeSpecialist("Dr. John", "Hematology", [patient1])
Now you can access the names and other attributes of the patients in a specialist's timetable:
for patient in specialist1.timetable:
print(patient.name)
You can also define a __repr__ method to tell Python how to display an object, rather than the current getPatient:
class makePatient(object):
# ...
def __repr__(self):
return "{0} (room {1})".format(self.name, self.room)
Now when you print the whole timetable:
>>> print(specialist1.timetable)
You get the necessary information:
[Michael (room 101)]
Note also that the classes should probably be called, simply, Patient, Specialist and Agenda; the make is implied.
Finally, you will get errors in makeAgenda.addAgenda as, without an __init__, self.timetable doesn't exist for a makeAgenda object, and an empty append() doesn't do anything anyway.
Classes are often used to represent entities and operations allowed on them, include constructing, or making, new instances of them. Therefore, your classes would be better named simplyPatient, Specialist, andAgenda. The name of the method that constructs a new instance of any class in Python is always__init__().
That said, after creating aPatientand aSpecialistyou could then add patient instances to the specialist's timetable/agenda by passing it to aSpecialistmethod specifically designed for that purpose. In other words, a Specialist "has-a" Agenda instance namedtimetableand to which patients can be added via an appropriately namedadd_to_timetable()method.
Here's what I mean -- note I've modified your code to follow PEP 8 -- Style Guide for Python Code guidelines which I also suggest that you follow:
class Agenda(object):
def __init__(self):
self.events = []
def append(self, event):
self.events.append(event)
class Patient(object):
def __init__(self, name, room):
self.name = name
self.room = room
def get_patient(self):
print(self.name)
print(self.room)
class Specialist(object):
def __init__(self, name, specialization):
self.name = name
self.specialization = specialization
self.timetable = Agenda()
def add_to_timetable(self, patient):
self.timetable.append(patient)
def get_specialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
specialist1 = Specialist("Dr. John", "Hematology")
patient1 = Patient("Michael", "101")
specialist1.add_to_timetable(patient1)
I'm not too sure what you're trying to accomplish here with method that just print values or with the makeAgenda class, but here's how you can get Michael in Dr. John's list:
specialist1.timetable.append(patient1.name)

assigning an object's name taken from expression

I need to extract data from external source. I build a base clas with some structure. I need objects with type of myBase class but with a name which automatically describes how many times I iterate over the source. I build a class which stores this number and with a call method which returns the object I need. I can't find a way to have a string as the name for the object but automatically differ every time I build new object.
I simplified the code for this example:
class myBase:
def __init__(self):
iteratedValue = None
class myIterator:
def __init__(self):
self.k = 0
def __call__(self, s):
self.k += 1
self.name = 'myData' + str(self.k)
# create an object named myData1, myData2 ...
self.name = myBase()
self.name.iteratedValue = s
print '%s name is %s for k = %i' % (self.name, self.name.iteratedValue, self.k)
# return object named myData1 type of myBase
return self.name
def DataExtraction():
# function to extract data form external source
data = myIterator()
data('Alice')
data('Ben')
DataExtraction()
My output is:
<__main__.myBase instance at 0x7f261b6dc6c8> name is Alice for k = 1
<__main__.myBase instance at 0x7f261b6dc6c8> name is Ben for k = 2
I need to return an object with a specific name and I expect an output:
myData1 name is Alice for k = 1
myData2 name is Ben for k = 2
The original problem is much more complicated. I have external data and every time they come I want to extract some values from this data. Every time I am doing this I need an object to work with it but I need this object with different names because on the end I store them in dict for another methods. In other words I work with data and store my results in the object myData1 when the original data change I work with it again and store the results in myData2 etc. After all I need all myData objects and do statistics on them to see how the change. I do not have access to original data any more. I need automatic name convention for myData and the best if it will express iterator.
How can I have a string in place of self.name as the name for an object?
It looks like you would be better off using generators. Here's some example code that reads data (actually it's hard-coded like in your example), and then construct Datum objects with the indexed name.
class Datum(object):
def __init__(self, index, value):
self.name = 'myData%d' % (index + 1)
self.value = value
def read_data():
yield 'Alice'
yield 'Ben'
def enumerate_data():
for i, value in enumerate(read_data()):
yield Datum(i, value)
for d in enumerate_data():
print d.name, d.value
You're already doing it with self.name = 'myData' + str(self.k). The problem is that you immediately overwrite it with self.name = myBase(). I'm not sure what you're trying to do with that myBase(), but you probably want to separate it from the name. self.name can be either a string or a myBase object, but it can't be both.
Perhaps you can do:
self.name = 'myData' + str(self.k)
self.base= myBase()
self.base.iteratedValue = s
print '%s name is %s for k = %i' % (self.name, self.baseiteratedValue, self.k)
This way you can have both the name and the "base", as separate attributes self.name and self.base.
Alternatively, you can give the myBase class a __str__ method. This will affect what shows up when you use print on a myBase object. However, to do that you'll have to pass in the desired name when you instantiate myBase, something like:
class myBase(object):
def __init__(self, name, iteratedValue):
self.name = name
iteratedValue = iteratedValue
def __str__(self):
return self.name
class myIterator(object):
def __init__(self):
self.k = 0
def __call__(self, s):
self.k += 1
name = 'myData' + str(self.k)
self.name = myBase(name, s)
print '%s name is %s for k = %i' % (self.name, self.name.iteratedValue, self.k)
# return object named myData1 type of myBase
return self.name
I'm not sure which of these ways (or perhaps some other way) is what you're looking for. What's puzzling is that you are making the "name" of the object be a myBase instance. I'm not sure what you're intending the "name" of your object to represent, but I wouldn't usually expect an object's name to be some other object.
Incidentally, it looks like you're using Python 2, in which case you should define your class with class someClass(object). Including the object makes your classes new-style classes, which is basically what you always want.
Edit: If what you're trying to do is actually create a variable called myData1 based on the string, so you can do myData('Alice') and then somehow magically have the variable myData1 refer to that object, the answer is "Don't do that." If you want to create a bunch of objects and access them in a structured way by numbers or other labels, use a list or a dictionary.
Why worry about naming your objects at all, why not just put them in a dict?
myObjects = {1:Alice(), 2:Bob()}

Categories