📜 ⬆️ ⬇️

Python Tips, Tricks, and Hacks (part 4, final)

This is the final part of the translation of the article. Decorators, switch for functions, some information about classes.

5. Functions


5.4 Decorators

The function decorators are fairly simple, but if you have not seen them before, it will be difficult to guess how they work. This is not obvious, unlike most Python syntax. A decorator is a function that wraps another function: first the main function is created, then it is passed to the decorator. The decorator returns a new function that is used instead of the original one in the rest of the program.

We will no longer linger on this. Here is the syntax:

def decorator1(func): return lambda: func() + 1 def decorator2(func): def print_func(): print func() return print_func @decorator2 @decorator1 def function(): return 41 function() #  "42" 

')
In this example, the function is passed to decorator1, and it returns a function that calls function and returns a number greater than its result by one. This function is passed to decorator2, which calls it and prints the result. So that.

The following example does the exact same thing, but more verbally:

 def decorator1(func): return lambda: func() + 1 def decorator2(func): def print_func(): print func() return print_func def function(): return 41 function = decorator2(decorator1(function)) function() #  "42" 

Typically, decorators use to add new features to your functions (see creating class methods). More often they are not used at all. But in order to understand the code, you need to know what it is.

To learn more about decorators, refer to the article by the author “Python Decorators” on the article “Python Decorators” on Dr. Dobbs or the "Function Definitions" section of the documentation .

5.5 Starting one of several functions using the dictionary

Not enough switch operator? You probably know that in Python there is no equivalent of a switch for functions (unless it is repeated elif). But you can reproduce its behavior by creating a dictionary of functions. For example, let you handle keystrokes and you have the following functions:

 def key_1_pressed(): print '  1' def key_2_pressed(): print '  2' def key_3_pressed(): print '  3' def unknown_key_pressed(): print '  ' 

The usual method is to use elif:

 keycode = 2 if keycode == 1: key_1_pressed() elif keycode == 2: key_2_pressed() elif number == 3: key_3_pressed() else: unknown_key_pressed() #  "  2" 

But you can also put all the functions in the dictionary, the keys of which are the values ​​of the corresponding keycode. You can even handle the case of receiving an unknown keycode:

 keycode = 2 functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed} functions.get(keycode, unknown_key_pressed)() 

Obviously, this code is much clearer than the previous one (especially with a large number of functions).

6. Classes


6.1 Transfer self manually

A method is a normal function: if it is called on an object instance, this instance is passed as the first argument (usually called self). If for some reason you are calling a method not on an instance, you can always pass the instance manually with the first argument. For example:

 class Class: def a_method(self): print ', !' instance = Class() instance.a_method() #  ', !',  .    : Class.a_method(instance) #  ', !' 

From the inside, these expressions are absolutely the same.

6.2 Testing for the existence of a method or property

Want to find out if an object has a particular method or property? You can use the hasattr built-in function, which accepts an object and an attribute name.

 class Class: answer = 42 hasattr(Class, 'answer') #  True hasattr(Class, 'question') #  False 

You can also check the existence of an attribute and get it using the built-in function getattr, which also accepts the object and attribute name. The third argument is optional and sets the default value. If the attribute is not found and the third argument is not specified, an AttributeError exception is thrown.

 class Class: answer = 42 getattr(Class, 'answer') #  42 getattr(Class, 'question', '  6x9?') #  "  6x9?" getattr(Class, 'question') #   AttributeError 

Do not use getattr and hasattr too often. If you wrote a class so that it is necessary to check the existence of properties, then you did everything wrong. The property must always exist, and if it is not used, you can set it to None. These functions are used mainly to support polymorphism, that is, the ability to use any type of object in your code.

6.3 Changing the class after creation

You can add, change, and delete properties and methods of a class after it has been created, even after instances of this class have been created. To do this, simply use the entry Class.attribute. Changes apply to all instances of the class, regardless of when they were created.

 class Class: def method(self): print ', ' instance = Class() instance.method() #  ", " def new_method(self): print '  !' Class.method = new_method instance.method() #  "  !" 

Fantasy. But do not get carried away by changing existing methods, it is a bad form. In addition, it can disrupt the work of methods using a mutable class. On the other hand, adding methods is less dangerous.

6.4 Creating class methods

Sometimes when writing a class there is a need for a function that is called from a class, and not its instance. Sometimes this method creates new instances, sometimes it does not depend on the individual properties of the instances. There are two ways to do this in Python, both of which work through decorators. Your choice depends on whether you need to know which class called the method.

A class method accepts the class that called it with the first argument (similarly, conventional methods receive the corresponding instance as the first argument). This method is known, it is launched from the same class or from a subclass.

The static method has no information about where it is started from. This is a common function, just in a different scope.

Both static methods and class methods can be called directly from the class:

 class Class: @classmethod def a_class_method(cls): print '   %s' % cls @staticmethod def a_static_method(): print '  ,  ' def an_instance_method(self): print '   %s' % self instance = Class() Class.a_class_method() instance.a_class_method() #   "   __main__.Class" Class.a_static_method() instance.a_static_method() #   "  ,  " Class.an_instance_method() #   TypeError instance.an_instance_method() #  -  "   <__main__.Class instance at 0x2e80d0>" 

Conclusion


Need more inspirational ideas? For a start, take a look at the Python Built-in Functions page. There are quite a few good features that you may have never heard of.

If you have any useful tricks or things you should know, the author suggests adding them to your article .

Happy programming.

With other articles of the author, as well as with the sources of information you can find here .

Full article in PDF

Source: https://habr.com/ru/post/95721/


All Articles