⬆️ ⬇️

Python interesting and useful

I’ve been programming in python for several years, however, I recently realized that many useful techniques and interesting moments passed by me, perhaps I’m not the only one, so I decided to list them here, I hope these techniques will be useful to someone at work or encourage them to meet with this language closer.



As in many languages ​​in python 1 is equivalent to True, and 0 is False, that is,



1 == True. 


It would seem, and what's wrong with that? However, this has some side effects due to the fact that identical objects must have the same hashes, respectively, you will not be able to cram the key 1 and True into one dictionary.



  >>> a = {1: "one", 0: "zero", True: "true", False: "false"} # -> {1: 'true', 0: 'false'} 


It also allows the following operations:

')

  >>> print(2 * False + True) # -> 1 


In this example, strings were used as dictionary values, however, I often want to use them as dictionary keys, I was always annoyed that when creating a dictionary using curly brackets, strings should be specified in quotes, I would like to omit them, it is possible if you create dictionary through the dict () constructor.



  >>> {"one": 1, "two": 2, "three": 3} == dict(one=1, two=2, three=3) # -> True 


In addition, using curly brackets creates not only dictionaries, but also sets (set).



  >>> a = {1, 2, 3} 


For the union of two sets, for some reason I want to use the + operator, probably because of the method of string concatenation. However, python does not support this operator for sets. But of course, this does not mean that we always have to use functions, the creators approached this issue more systematically and added support for basic operations on sets (and not just unions) to the language and “hung” them on logical operators .



 a = {1, 2, 3} b = {0, 2, 4} print(a & b) # -> {2} print(a | b) # -> {0, 1, 2, 3, 4} print(a ^ b) # -> {0, 1, 3, 4} print(a - b) # -> {1, 3},    #     


Continuing the conversation about dictionaries, starting with version 3.7, the language specification ensures that dictionaries preserve the order of insertion of elements, OrderedDict is no longer needed.



www.python.org/downloads/release/python-370

mail.python.org/pipermail/python-dev/2017-December/151283.html



 d = dict(zero='Cero', one='Uno', two='Dos', three='Tres', four='Cuatro', five='Cinco', six='Seis', seven='Siete', eight='Ocho', night='Nueve') for index, (key, value) in enumerate(d.items()): print(f"{index} is {key} in England and {value} in Spain") 


Notice the output string, it starts with the prefix f - this is a special type of string, entered in python 3.6 .



There are three types of strings in the language: normal, indicated by quotes without prefixes, raw \ not processed (raw), in which special characters like \ n are not processed and inserted as text and f-lines themselves.



They were created to simplify the output, python supports a huge number of output methods:



 print("result" + str(2)) #   , python   #      #  ,     print("result", 2) # print      , #        , #        , #      print("result %d" % 2) # %-,      C. print("result %d %.2f" % (2, 2)) # https://docs.python.org/3.4/library/string.html#formatspec print("result %(name)s" % {"name": 2}) #      print("{}".format(2)) #      format() #       print("{0} {1} {0}".format(1, 2)) #          #     #     #        ,    print("{} {}".format(2)) # -> IndexError: tuple index out of range print("{0} {0}".format(2, 3)) # -> 2 2       #     from math import pi #         print("{:.2f}".format(pi)) # -> 3.14 from string import Template #      s = Template("result $res") #       print(s.substitute(res = [3, 4])) 


Now add more f-lines. Any variables from the scope are available in them, you can call functions, get elements by key, in addition, they support format strings.



 from math import pi result = 4 name = "user" print(f"{name:84s} pi= {pi:.2f}, result={result}, {name[2]}") # -> user pi= 3.14, result=4, e from datetime import datetime print(f"{datetime.now():%Y:%m-%d}") 


They are the fastest of all other output methods, so if python3.6 is available to you, it is recommended that you use them.



One of the coolest python chips is that it is not objects and primitives that are packed and unpacked, but parameters and collections.



 def func(*argv, **kwargs) 


However, there is one architectural flaw in the implementation:





The disadvantage, of course, is not big, but it’s still unpleasant that you cannot directly transfer kwargs to a cache based on a dictionary, on the other hand, if you add a list to a tuple, then such a tuple cannot be added to the dictionary either.



Sets are also created on the basis of a hash table, this means that the values ​​must be hashed, besides the set itself is a mutable and non-hash type, there is a special type frozenset - not a mutable set (do not ask me why it is needed).



We discussed the creation of the type frozendict, but so far it has not been added (although at least one application already exists for it - as kwargs). For an immutable dictionary, you have to take the rap on namedtuple . And also for writing and simple classes.



In his student / school years, who wrote cycles to output the values ​​of the array and was furious because of the comma at the end, each time he decided to score or rewrite it to be beautiful, and only in the course of 2-3 did he learn about the join method? Or am I the only one?



One of the unpleasant features of the join method is with strings - it works only with string elements, if the collection has at least one non-string, you have to use a generator expression, which looks too complicated to solve such a simple task, however, there is a way to simplify the output of list values ​​(without parentheses) .



 a = list(range(5)) print(" ".join(a)) # -> TypeError: sequence item 0: expected str instance, int found print(" ".join(str(i) for i in a)) # -> 0 1 2 3 4 print(*a) # -> 0 1 2 3 4 


Since the strings are collections too, they can also be “jointed”.



 print('-'.join("hello")) # -> hello 


Consider the line from the previous example.



 print(" ".join(str(i) for i in a)) # -> 0 1 2 3 4 


The generator expression is passed to the join function without any brackets; parentheses can be omitted to simplify reading the code. Python takes care of expressiveness.



 print(sum(i**2 for i in range(10))) # -> 285 


In addition, parentheses can be omitted when creating tuples:



 article = "python", 2018, "LinearLeopard" #   theme, year, author = "python", 2018, "LinearLeopard"#   theme, year, _ = "python", 2018, "LinearLeopard" #     #    # ,  , #  -   , #    #  theme, _, _ = "python", 2018, "LinearLeopard" #    theme, *, author = "python", 2018, "LinearLeopard" #    # ,   #  , # ,  #   #   


The asterisk can also be used in function declarations, thus it is possible to create parameters that can be specified only by key .



 def sortwords(*wordlist, case_sensitive=False): 


You can transfer to the function as many parameters as you like without fear that one of them will be perceived as the value of the case_sensitive parameter.



Could be so.



 def func(first, second, *, kwonly): 




Let's take a closer look at how it simply * differs from * args.



 def func(first, second, *, kwonly=True): print(first, second, kwonly) def func2(first, second, *args, kwonly=True): print(first, second, *args, kwonly) func(1) #-> TypeError: func() missing 1 required positional argument: 'second' func(1, 2) #-> 1 2 True func(1, 2, False) #-> TypeError: func() takes 2 positional arguments but 3 were given #  *    ,  #        #   func(1, 2, kwonly=False) #-> 1 2 False func2(1, 2, False) #-> 1 2 False True # *args      # ,       #    (>2)  #  




One interesting feature is connected with the default parameters: they are calculated at the stage of the module compilation into bytecode, so it is better not to use variable types there. We declare a function that adds an element to the end of the list, if the second argument is omitted, the function returns a new list that contains only this element.



 def add_to(elem, collection=[]): collection.append(elem) return collection a = ["a", "c"] print(add_to("b", a)) # -> ['a', 'c', 'b'] print(add_to("a")) # -> ['a'] print(add_to("b")) # -> ['a', 'b']   'a'? 


The default values ​​of the f-tion stored in the field __defaults__, you can at any time to find out what is there.



 print(add_to.__defaults__) # -> (['a', 'b'],) 


Since the default argument (an empty list) was created at the time the program was started and is not recreated every time, we received exactly this behavior.



You can correct this behavior if you make the default value an unchangeable type, and create a list in the function body:



 def add_to(elem, collection=None): collection = collection or [] collection.append(elem) return collection 


Pay attention to the team



 collection = collection or [] 


it is a shorter (and less understandable, though not for all) analogue



 collection = collection if collection else [] 




Link to the next part

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



All Articles