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:2.5 [05/11/2023 21:33] – suprimit - edició externa (Unknown date) 127.0.0.1 | info:cursos:pue:python-pcpp1:m1:2.5 [05/11/2023 21:33] (actual) – ↷ Page moved from info:cursos:pue:python-pcpp1:2.5 to info:cursos:pue:python-pcpp1:m1:2.5 mate | ||
|---|---|---|---|
| Línia 1: | Línia 1: | ||
| + | = 2.5 Different faces of Python methods | ||
| + | Until now, we’ve been implementing methods that have performed operations on the instances (objects), and in particular the attributes of the instance, so we’ve called them i**nstance methods**. | ||
| + | |||
| + | The instance methods, as the first parameter, take the '' | ||
| + | |||
| + | The code in the editor demonstrates the idea presented above. | ||
| + | |||
| + | <code python> | ||
| + | class Example: | ||
| + | def __init__(self, | ||
| + | self.__internal = value | ||
| + | |||
| + | def get_internal(self): | ||
| + | return self.__internal | ||
| + | |||
| + | example1 = Example(10) | ||
| + | example2 = Example(99) | ||
| + | print(example1.get_internal()) | ||
| + | print(example2.get_internal()) | ||
| + | </ | ||
| + | |||
| + | Each of the Example class objects has its own copy of the instance variable '' | ||
| + | |||
| + | The name of the parameter '' | ||
| + | |||
| + | <code ; output> | ||
| + | 10 | ||
| + | 99 | ||
| + | </ | ||
| + | |||
| + | == The static and class methods | ||
| + | Two other types of method can also be used in the Object Oriented Approach (OOP): | ||
| + | |||
| + | * class methods; | ||
| + | * static methods. | ||
| + | |||
| + | These alternative types of method should be understood as tool methods, extending our ability to use classes, not necessarily requiring the creation of class instances to use them. | ||
| + | |||
| + | As a result, our perception of the Python class concept is extended by two types of specialized methods. | ||
| + | |||
| + | == Class methods | ||
| + | Class methods are methods that, like class variables, work on the class itself, and not on the class objects that are instantiated. You can say that they are methods bound to the class, not to the object. | ||
| + | |||
| + | When can this be useful? | ||
| + | |||
| + | There are several possibilities, | ||
| + | - we control access to class variables, e.g., to a class variable containing information about the number of created instances or the serial number given to the last produced object, or we modify the state of the class variables; | ||
| + | - we need to create a class instance in an alternative way, so the class method can be implemented by an alternative constructor. | ||
| + | |||
| + | <code python> | ||
| + | class Example: | ||
| + | __internal_counter = 0 | ||
| + | |||
| + | def __init__(self, | ||
| + | Example.__internal_counter +=1 | ||
| + | |||
| + | @classmethod | ||
| + | def get_internal(cls): | ||
| + | return '# of objects created: {}' | ||
| + | |||
| + | print(Example.get_internal()) | ||
| + | |||
| + | example1 = Example(10) | ||
| + | print(Example.get_internal()) | ||
| + | |||
| + | example2 = Example(99) | ||
| + | print(Example.get_internal()) | ||
| + | </ | ||
| + | |||
| + | Convention | ||
| + | * To be able to distinguish a class method from an instance method, the programmer signals it with the '' | ||
| + | * Additionally, | ||
| + | |||
| + | As with //self//, //cls// was chosen arbitrarily (i.e., you can use a different name, but you must do it consistently). | ||
| + | |||
| + | You can find an example of a class method implementation in the editor. | ||
| + | |||
| + | The result of the code: | ||
| + | <code ; output> | ||
| + | # of objects created: 0 | ||
| + | # of objects created: 1 | ||
| + | # of objects created: 2 | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | Of course, you can use the reference to “%%Example.__internal_counter%%”, | ||
| + | |||
| + | An exception is the '' | ||
| + | |||
| + | The code presented in the editor shows how to use the class method as an alternative constructor, | ||
| + | |||
| + | <code python> | ||
| + | class Car: | ||
| + | def __init__(self, | ||
| + | print(' | ||
| + | self.vin = vin | ||
| + | self.brand = '' | ||
| + | |||
| + | @classmethod | ||
| + | def including_brand(cls, | ||
| + | print(' | ||
| + | _car = cls(vin) | ||
| + | _car.brand = brand | ||
| + | return _car | ||
| + | |||
| + | car1 = Car(' | ||
| + | car2 = Car.including_brand(' | ||
| + | |||
| + | print(car1.vin, | ||
| + | print(car2.vin, | ||
| + | |||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | In accordance with the convention, the creation of a class object (i.e., calling the '' | ||
| + | |||
| + | Then the class method performs an additional task – in this case, it supplements the brand instance variable and finally returns the newly created object. | ||
| + | |||
| + | The result of the presented code: | ||
| + | <code ; output> | ||
| + | Ordinary __init__ was called for ABCD1234 | ||
| + | Class method was called | ||
| + | Ordinary __init__ was called for DEF567 | ||
| + | ABCD1234 | ||
| + | DEF567 NewBrand | ||
| + | </ | ||
| + | As you can see, for the car with vin=ABCD1234, | ||
| + | |||
| + | For the car with vin=DEF567, the class method has been called, which calls the '' | ||
| + | |||
| + | == Static methods | ||
| + | Static methods are methods that do not require (and do not expect!) a parameter indicating the class object or the class itself in order to execute their code. | ||
| + | |||
| + | When can it be useful? | ||
| + | |||
| + | * When you need a utility method that comes in a class because it is semantically related, but does not require an object of that class to execute its code; | ||
| + | * consequently, | ||
| + | Convention | ||
| + | * To be able to distinguish a static method from a class method or instance method, the programmer signals it with the '' | ||
| + | * Static methods do not have the ability to modify the state of objects or classes, because they lack the parameters that would allow this. | ||
| + | |||
| + | <code python> | ||
| + | class Bank_Account: | ||
| + | def __init__(self, | ||
| + | print(' | ||
| + | self.iban = iban | ||
| + | | ||
| + | @staticmethod | ||
| + | def validate(iban): | ||
| + | if len(iban) == 20: | ||
| + | return True | ||
| + | else: | ||
| + | return False | ||
| + | |||
| + | </ | ||
| + | | ||
| + | **An example of using the static method** | ||
| + | |||
| + | Imagine a class that represents a bank account, that is, a class that provides methods to operate on bank accounts. This may include a method that validates the correctness of the account number recorded in accordance with the IBAN standard. | ||
| + | |||
| + | This is a great place to introduce a static method, which, provided by the bank account class, will be used to validate the character string and will answer the question: can a given character string be an account number before the object is created? | ||
| + | |||
| + | To shorten the size of the sample code, the static method responsible for validation checks only the length of the string, and only those numbers whose length is 20 characters are treated as valid. | ||
| + | |||
| + | Note that for the purpose of validating three different character strings, it isn’t necessary to create class objects. | ||
| + | |||
| + | <code python> | ||
| + | class Bank_Account: | ||
| + | def __init__(self, | ||
| + | print(' | ||
| + | self.iban = iban | ||
| + | | ||
| + | @staticmethod | ||
| + | def validate(iban): | ||
| + | if len(iban) == 20: | ||
| + | return True | ||
| + | else: | ||
| + | return False | ||
| + | |||
| + | |||
| + | account_numbers = [' | ||
| + | |||
| + | for element in account_numbers: | ||
| + | if Bank_Account.validate(element): | ||
| + | print(' | ||
| + | else: | ||
| + | print(' | ||
| + | |||
| + | </ | ||
| + | |||
| + | The result of the code presented: | ||
| + | <code ; output> | ||
| + | We can use 88888888888888888888 | ||
| + | The account number 7777 is invalid | ||
| + | The account number 2222 is invalid | ||
| + | </ | ||
| + | |||
| + | == Using static and class methods - comparison | ||
| + | The time has come to compare the use of class and static methods: | ||
| + | |||
| + | - a class method requires ' | ||
| + | - a class method has the ability to access the state or methods of the class, and a static method does not; | ||
| + | - a class method is decorated by '' | ||
| + | - a class method can be used as an alternative way to create objects, and a static method is only a utility method. | ||
| + | |||
| + | == lab | ||
| + | === Objectives | ||
| + | * improving the student' | ||
| + | === Scenario | ||
| + | Create a class representing a luxury watch; | ||
| + | * The class should allow you to hold a number of watches created in the '' | ||
| + | * the class may allow you to create a watch with a dedicated engraving (text). As this is an extra option, the watch with the engraving should be created using an alternative constructor (a class method), as a regular '' | ||
| + | * the regular '' | ||
| + | The text intended to be engraved should follow some restrictions: | ||
| + | |||
| + | * it should not be longer than 40 characters; | ||
| + | * it should consist of alphanumerical characters, so no space characters are allowed; | ||
| + | * if the text does not comply with restrictions, | ||
| + | |||
| + | before engraving the desired text, the text should be validated against restrictions using a dedicated static method. | ||
| + | |||
| + | * Create a watch with no engraving | ||
| + | * Create a watch with correct text for engraving | ||
| + | * Try to create a watch with incorrect text, like ' | ||
| + | * After each watch is created, call class method to see if the counter variable was increased | ||
| + | |||
| + | === resposta | ||
| + | <code python> | ||
| + | class LuxuryWatch: | ||
| + | watches_created = 0 | ||
| + | | ||
| + | def __init__(self): | ||
| + | LuxuryWatch.watches_created +=1 | ||
| + | | ||
| + | def get_number_of_watches_created(self): | ||
| + | return LuxuryWatch.watches_created | ||
| + | | ||
| + | @classmethod | ||
| + | def set_engrabing(cls, | ||
| + | if LuxuryWatch.check_text(text): | ||
| + | _watch = cls() | ||
| + | _watch.text = text | ||
| + | return _watch | ||
| + | else: | ||
| + | raise ValueError(' | ||
| + | | ||
| + | @staticmethod | ||
| + | def check_text(text): | ||
| + | retorn = False | ||
| + | | ||
| + | if len(text) <= 40 and text.isalnum(): | ||
| + | retorn = True | ||
| + | | ||
| + | return retorn | ||
| + | |||
| + | try: | ||
| + | w1 = LuxuryWatch() | ||
| + | print(w1.get_number_of_watches_created()) | ||
| + | w2 = LuxuryWatch.set_engrabing(" | ||
| + | print(w2.get_number_of_watches_created()) | ||
| + | w3 = LuxuryWatch.set_engrabing(" | ||
| + | print(w2.get_number_of_watches_created()) | ||
| + | except ValueError as err: | ||
| + | print(" | ||
| + | </ | ||