Diferències
Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
| Ambdós costats versió prèvia Revisió prèvia | |||
| info:cursos:pue:python-pcpp1:m1:1.2 [05/11/2023 21:32] – suprimit - edició externa (Unknown date) 127.0.0.1 | info:cursos:pue:python-pcpp1:m1:1.2 [05/11/2023 21:32] (actual) – ↷ Page moved from info:cursos:pue:python-pcpp1:1.2 to info:cursos:pue:python-pcpp1:m1:1.2 mate | ||
|---|---|---|---|
| Línia 1: | Línia 1: | ||
| + | = 1.2 Working with class and instance data – instance variables | ||
| + | == Instance variables | ||
| + | This kind of variable exists when and only when it is explicitly created and added to an object. This can be done during the object' | ||
| + | Each object carries its own set of variables – they don't interfere with one another in any way. The word instance suggests that they are closely connected to the objects (which are class instances), not to the classes themselves. To get access to the instance variable, you should address the variable in the following way: objectdotvariable_name. | ||
| + | |||
| + | Let's look at how the instance variable is created and accessed in the code presented below. | ||
| + | |||
| + | <code python> | ||
| + | class Demo: | ||
| + | def __init__(self, | ||
| + | self.instance_var = value | ||
| + | |||
| + | d1 = Demo(100) | ||
| + | d2 = Demo(200) | ||
| + | |||
| + | print(" | ||
| + | print(" | ||
| + | </ | ||
| + | - **%%__init__%%** creates an instance_var variable for the instance. The keyword self is used to indicate that this variable is created coherently and individually for the instance to make it independent from other instances of the same class; | ||
| + | - we instantiate the class twice, each time passing a different value to be stored inside the object; | ||
| + | - the print instructions prove the fact that instance variable values are kept independently, | ||
| + | |||
| + | Another snippet shows that instance variables can be created during any moment of an object' | ||
| + | |||
| + | <code python> | ||
| + | class Demo: | ||
| + | def __init__(self, | ||
| + | self.instance_var = value | ||
| + | |||
| + | d1 = Demo(100) | ||
| + | d2 = Demo(200) | ||
| + | |||
| + | d1.another_var = ' | ||
| + | |||
| + | print(' | ||
| + | print(' | ||
| + | </ | ||
| + | |||
| + | This example shows that modifying the instance variable of any object has no impact on all the remaining objects. Instance variables are completely isolated from each other. | ||
| + | |||
| + | <code ; output> | ||
| + | contents of d1: {' | ||
| + | contents of d2: {' | ||
| + | </ | ||
| + | |||
| + | == Class variables | ||
| + | Class variables are defined within the class construction, | ||
| + | |||
| + | <code python> | ||
| + | class Demo: | ||
| + | class_var = ' | ||
| + | |||
| + | print(Demo.class_var) | ||
| + | print(Demo.__dict__) | ||
| + | </ | ||
| + | Similarly to instance variables, class variables are shown in the class' | ||
| + | <code ; output> | ||
| + | shared variable | ||
| + | {' | ||
| + | </ | ||
| + | |||
| + | As a class variable is present before any instance of the class is created, it can be used to store some meta data relevant to the class, rather than to the instances: | ||
| + | |||
| + | * fixed information like description, | ||
| + | * mutable information like the number of instances created (if we add a code to increment the value of a designated variable every time we create a class instance) | ||
| + | |||
| + | A class variable is a class property that exists in just one copy, and it is stored outside any class instance. Because it is owned by the class itself, all class variables are shared by all instances of the class. They will therefore generally have the same value for every instance; butas the class variable is defined outside the object, it is not listed in the object' | ||
| + | |||
| + | <code python> | ||
| + | class Demo: | ||
| + | class_var = ' | ||
| + | |||
| + | d1 = Demo() | ||
| + | d2 = Demo() | ||
| + | |||
| + | print(Demo.class_var) | ||
| + | print(d1.class_var) | ||
| + | print(d2.class_var) | ||
| + | |||
| + | print(' | ||
| + | </ | ||
| + | <code ; output> | ||
| + | shared variable | ||
| + | shared variable | ||
| + | shared variable | ||
| + | contents of d1: {} | ||
| + | </ | ||
| + | |||
| + | **Conclusion**: | ||
| + | |||
| + | When you want to set or change a value of the class variable, you should access it via the class, but not the class instance, as you can do for reading. | ||
| + | |||
| + | When you try to set a value for the class variable using the object (a variable referring to the object or '' | ||
| + | |||
| + | The snippet contains additional comments for direct explanations. | ||
| + | |||
| + | <code python> | ||
| + | class Demo: | ||
| + | class_var = ' | ||
| + | |||
| + | d1 = Demo() | ||
| + | d2 = Demo() | ||
| + | |||
| + | # both instances allow access to the class variable | ||
| + | print(d1.class_var) | ||
| + | print(d2.class_var) | ||
| + | print(' | ||
| + | |||
| + | # d1 object has no instance variable | ||
| + | print(' | ||
| + | print(' | ||
| + | |||
| + | # d1 object receives an instance variable named ' | ||
| + | d1.class_var = " | ||
| + | |||
| + | # d1 object owns the variable named ' | ||
| + | print(' | ||
| + | print(d1.class_var) | ||
| + | print(' | ||
| + | |||
| + | # d2 object variables were not influenced | ||
| + | print(' | ||
| + | |||
| + | # d2 object variables were not influenced | ||
| + | print(' | ||
| + | </ | ||
| + | |||
| + | <code ; output> | ||
| + | shared variable | ||
| + | shared variable | ||
| + | .................... | ||
| + | contents of d1: {} | ||
| + | .................... | ||
| + | contents of d1: {' | ||
| + | I'm messing with the class variable | ||
| + | .................... | ||
| + | contents of d2: {} | ||
| + | contents of class variable accessed via d2: shared variable | ||
| + | </ | ||
| + | |||
| + | Class variables and instance variables are often utilized at the same time, but for different purposes. As mentioned before, class variables can refer to some meta information or common information shared amongst instances of the same class. | ||
| + | |||
| + | The example below demonstrates both topics: each class owns a counter variable that holds the number of class instances created. Moreover, each class owns information that helps identify the class instance origins. Similar functionality could be achieved with the '' | ||
| + | |||
| + | Both the **Duck** and **Chicken** classes own a class variable named **species**, | ||
| + | |||
| + | <code python> | ||
| + | class Duck: | ||
| + | counter = 0 | ||
| + | species = ' | ||
| + | |||
| + | def __init__(self, | ||
| + | self.height = height | ||
| + | self.weight = weight | ||
| + | self.sex = sex | ||
| + | Duck.counter +=1 | ||
| + | |||
| + | def walk(self): | ||
| + | pass | ||
| + | |||
| + | def quack(self): | ||
| + | print(' | ||
| + | |||
| + | class Chicken: | ||
| + | species = ' | ||
| + | |||
| + | def walk(self): | ||
| + | pass | ||
| + | |||
| + | def cluck(self): | ||
| + | print(' | ||
| + | |||
| + | duckling = Duck(height=10, | ||
| + | drake = Duck(height=25, | ||
| + | hen = Duck(height=20, | ||
| + | |||
| + | chicken = Chicken() | ||
| + | |||
| + | print(' | ||
| + | |||
| + | for poultry in duckling, drake, hen, chicken: | ||
| + | print(poultry.species, | ||
| + | if poultry.species == ' | ||
| + | poultry.quack() | ||
| + | elif poultry.species == ' | ||
| + | poultry.cluck() | ||
| + | </ | ||
| + | <code ; output> | ||
| + | duck quacks | ||
| + | duck quacks | ||
| + | duck quacks | ||
| + | chicken clucks</ | ||
| + | |||
| + | Another example shows that a class variable of a super class can be used to count the number of all objects created from the descendant classes (subclasses). We'll achieve this by calling the superclass %%__init__%% method. | ||
| + | |||
| + | Another class variable is used to keep track of the serial numbers (which in fact are also counters) of particular subclass instances. In this example, we are also storing instance data (phone numbers) in instance variables. | ||
| + | |||
| + | The class **Phone** is a class representing a blueprint of generic devices used for calling. This class definition delivers the **call** method, which displays the object’s variable, which holds the phone number. This class also holds a class variable that is used to count the number of instances created by its subclasses. | ||
| + | |||
| + | Subclasses make use of the superclass %%__init__%% method, then instances are created. This gives us the possibility to increment the superclass variable. | ||
| + | |||
| + | <code python> | ||
| + | class Phone: | ||
| + | counter = 0 | ||
| + | |||
| + | def __init__(self, | ||
| + | self.number = number | ||
| + | Phone.counter += 1 | ||
| + | |||
| + | def call(self, number): | ||
| + | message = ' | ||
| + | return message | ||
| + | |||
| + | |||
| + | class FixedPhone(Phone): | ||
| + | last_SN = 0 | ||
| + | |||
| + | def __init__(self, | ||
| + | super().__init__(number) | ||
| + | FixedPhone.last_SN += 1 | ||
| + | self.SN = ' | ||
| + | |||
| + | |||
| + | class MobilePhone(Phone): | ||
| + | last_SN = 0 | ||
| + | |||
| + | def __init__(self, | ||
| + | super().__init__(number) | ||
| + | MobilePhone.last_SN += 1 | ||
| + | self.SN = ' | ||
| + | |||
| + | |||
| + | print(' | ||
| + | print(' | ||
| + | fphone = FixedPhone(' | ||
| + | mphone = MobilePhone(' | ||
| + | |||
| + | print(' | ||
| + | print(' | ||
| + | |||
| + | print(fphone.call(' | ||
| + | print(' | ||
| + | print(' | ||
| + | </ | ||
| + | <code ; output> | ||
| + | Total number of phone devices created: 0 | ||
| + | Creating 2 devices | ||
| + | Total number of phone devices created: 2 | ||
| + | Total number of mobile phones created: 1 | ||
| + | Calling 01632-960004 using own number 555-2368 | ||
| + | Fixed phone received " | ||
| + | Mobile phone received " | ||
| + | </ | ||
| + | |||
| + | === LAB | ||
| + | Scenario | ||
| + | Imagine that you receive a task description of an application that monitors the process of apple packaging before the apples are sent to a shop. | ||
| + | |||
| + | A shop owner has asked for 1000 apples, but the total weight limitation cannot exceed 300 units. | ||
| + | |||
| + | Write a code that creates objects representing apples as long as both limitations are met. When any limitation is exceeded, than the packaging process is stopped, and your application should print the number of apple class objects created, and the total weight. | ||
| + | |||
| + | Your application should keep track of two parameters: | ||
| + | |||
| + | * the number of apples processed, stored as a class variable; | ||
| + | * the total weight of the apples processed; stored as a class variable. Assume that each apple' | ||
| + | |||
| + | Hint: Use a '' | ||
| + | |||
| + | <code python> | ||
| + | import random | ||
| + | |||
| + | class Pomes: | ||
| + | total_w = 0 | ||
| + | total_i = 0 | ||
| + | | ||
| + | def __init__(self): | ||
| + | if (Pomes.total_w > 300): | ||
| + | print(" | ||
| + | else: | ||
| + | self.weight = random.uniform(0.2, | ||
| + | Pomes.total_w += self.weight | ||
| + | Pomes.total_i += 1 | ||
| + | | ||
| + | |||
| + | |||
| + | for i in range(1000): | ||
| + | Pomes() | ||
| + | </ | ||