17 questions you should know about Python programmers you don't know a lot about

  • 2020-04-02 13:40:55
  • OfStack

Do not use mutable objects as function defaults

In [1]: def append_to_list(value, def_list=[]):
   ...:         def_list.append(value)
   ...:         return def_list
   ...:
In [2]: my_list = append_to_list(1)
In [3]: my_list
Out[3]: [1]
In [4]: my_other_list = append_to_list(2)
In [5]: my_other_list
Out[5]: [1, 2] #  See, we just wanted to generate [2]  But it brings in the effects page from the first run 
In [6]: import time
In [7]: def report_arg(my_default=time.time()):
   ...:         print(my_default)
   ...:
In [8]: report_arg() #  First execution 
1399562371.32
In [9]: time.sleep(2) #  After a 2 seconds 
In [10]: report_arg()
1399562371.32 #  The time didn't change 

What do these two examples show? Dictionary, set, list, and so on object function is not suitable as a default value. Because of this default value function to establish generated when I was really, every call is done with the object of "cache". I share the python in the period of senior programming also said the problem, this is the actual problems of the development, to check the code you've learned well, maybe it's just not relevant

You can change it like this:


def append_to_list(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

Second, the generator does not retain the results after the iteration

In [12]: gen = (i for i in range(5))
In [13]: 2 in gen
Out[13]: True
In [14]: 3 in gen
Out[14]: True
In [15]: 1 in gen
Out[15]: False # 1 Why not gen Inside the ?  Because of the call 1->2, At this time 1 It's not in the iterator anymore , It was generated on demand 
In [20]: gen = (i for i in range(5))
In [21]: a_list = list(gen) #  You can convert to lists, of course a_tuple = tuple(gen)  Can also be 
In [22]: 2 in a_list
Out[22]: True
In [23]: 3 in a_list
Out[23]: True
In [24]: 1 in a_list #  Even if it goes through , Value is still in 
Out[24]: True

Lambda stores local variables in closures


In [29]: my_list = [lambda: i for i in range(5)]
In [30]: for l in my_list:
   ....:         print(l())
   ....:
4
4
4
4
4

The reason for this is the same as in python advanced programming, which is that when I assign a value to my_list,lambda will loop through I until I =4, and I will remain

But you can use a generator


In [31]: my_gen = (lambda: n for n in range(5))
In [32]: for l in my_gen:
   ....:         print(l())
   ....:
0
1
2
3
4

You can also stick to the list:

In [33]: my_list = [lambda x=i: x for i in range(5)] #  Watch me give each one lambda The expression assigns a default value 
In [34]: for l in my_list:
   ....:         print(l())
   ....:
0
1
2
3
4

It's a little hard to understand, isn't it? Here's another python magic:

In [35]: def groupby(items, size):
   ....:     return zip(*[iter(items)]*size)
   ....:
In [36]: groupby(range(9), 3)
Out[36]: [(0, 1, 2), (3, 4, 5), (6, 7, 8)]

It's a grouping function, it's a little hard to understand, right? So let's parse this

In [39]: [iter(items)]*3
Out[39]:
[<listiterator at 0x10e155fd0>,
 <listiterator at 0x10e155fd0>,
 <listiterator at 0x10e155fd0>] #  see ,  It's just a way of items To be iterative ,  Repeat three times (same object) ,  But don't forget , Each time, .next(),  So it's a grouping 
 In [40]: [lambda x=i: x for i in range(5)]
Out[40]:
[<function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>] #  See you understand ?


Modify list items in a loop

In [44]: a = [1, 2, 3, 4, 5]
In [45]: for i in a:
   ....:     if not i % 2:
   ....:         a.remove(i)
   ....:
In [46]: a
Out[46]: [1, 3, 5] #  There is no problem 
In [50]: b = [2, 4, 5, 6]
In [51]: for i in b:
   ....:      if not i % 2:
   ....:          b.remove(i)
   ....:
In [52]: b
Out[52]: [4, 5] #  What I would have wanted was a list with even Numbers removed 

Think about it, why does the remove from the list affect its index

In [53]: b = [2, 4, 5, 6]
In [54]: for index, item in enumerate(b):
   ....:     print(index, item)
   ....:     if not item % 2:
   ....:         b.remove(item)
   ....:
(0, 2) #  There is no problem here  2 Been deleted 
(1, 5) #  because 2 The deleted current list is [4, 5, 6],  So the index list[1] Directly to find 5,  Ignore the 4
(2, 6)

5. IndexError - the list takes values beyond his index


In [55]: my_list = [1, 2, 3, 4, 5]
In [56]: my_list[5] #  There is no element 
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-56-037d00de8360> in <module>()
----> 1 my_list[5]
IndexError: list index out of range #  The thrown exception 
In [57]: my_list[5:] #  But you can do this,   Be careful ,  Is with good trick, If you use it the wrong way, it's a hole 
Out[57]: []

Reuse global variables


In [58]: def my_func():
   ....:         print(var) #  I can call an undefined variable first 
   ....:
In [59]: var = 'global' #  After the assignment 
In [60]: my_func() #  Anyway, as long as the variable is defined when the function is called 
global
In [61]: def my_func():
   ....:     var = 'locally changed'
   ....:
In [62]: var = 'global'
In [63]: my_func()
In [64]: print(var)
global #  Local variables do not affect global variables 
In [65]: def my_func():
   ....:         print(var) #  Although you set this variable globally ,  But local variables have the same name , python I thought you forgot to define a local variable 
   ....:         var = 'locally changed'
   ....:
In [66]: var = 'global'
In [67]: my_func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-67-d82eda95de40> in <module>()
----> 1 my_func()
<ipython-input-65-0ad11d690936> in my_func()
      1 def my_func():
----> 2         print(var)
      3         var = 'locally changed'
      4
UnboundLocalError: local variable 'var' referenced before assignment
In [68]: def my_func():
   ....:         global var #  It's time to add the big picture 
   ....:         print(var) #  So it works 
   ....:         var = 'locally changed'
   ....:
In [69]: var = 'global'
In [70]:
In [70]: my_func()
global
In [71]: print(var)
locally changed #  But using global I changed the global variable 

Copy mutable objects

In [72]: my_list1 = [[1, 2, 3]] * 2
In [73]: my_list1
Out[73]: [[1, 2, 3], [1, 2, 3]]
In [74]: my_list1[1][0] = 'a' #  I only modify one item in the sublist 
In [75]: my_list1
Out[75]: [['a', 2, 3], ['a', 2, 3]] #  But it does 
In [76]: my_list2 = [[1, 2, 3] for i in range(2)] #  The method of generating different objects with this loop does not matter 
In [77]: my_list2[1][0] = 'a'
In [78]: my_list2
Out[78]: [[1, 2, 3], ['a', 2, 3]]


8. Python multiple inheritance (C3)


In [1]: class A(object):
   ...:         def foo(self):
   ...:                 print("class A")
   ...:
In [2]: class B(object):
   ...:         def foo(self):
   ...:                 print("class B")
   ...:
In [3]: class C(A, B):
   ...:         pass
   ...:
In [4]: C().foo()
class A #  The examples are easy to follow , C inherited A and B From left to right , found A There are foo methods , Returned to the 

It all seems very simple, in order from the bottom up, from front to back, and then back.


In [5]: class A(object):
   ...:        def foo(self):
   ...:               print("class A")
   ...:
In [6]: class B(A):
   ...:        pass
   ...:
In [7]: class C(A):
   ...:        def foo(self):
   ...:               print("class C")
   ...:
In [8]: class D(B,C):
   ...:        pass
   ...:
In [9]: D().foo()
class C # ?  According to the truth ,  The order is  D->B->A, Why did you find it? C Where's the 

This also involves MRO(Method Resolution Order):

In [10]: D.__mro__
Out[10]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Simple to understand is that the new class is breadth first, D- > B, but when you find out that C inherits A, you look for C, and then you look for A

Nine, the list of + and +=, append and extend

In [17]: print('ID:', id(a_list))
('ID:', 4481323592)
In [18]: a_list += [1]
In [19]: print('ID (+=):', id(a_list))
('ID (+=):', 4481323592) #  use +=  I'm going to do it on the same list 
In [20]: a_list = a_list + [2]
In [21]: print('ID (list = list + ...):', id(a_list))
('ID (list = list + ...):', 4481293056) #  simple + I've actually changed the list 
In [28]: a_list = []
In [29]: id(a_list)
Out[29]: 4481326976
In [30]: a_list.append(1)
In [31]: id(a_list)
Out[31]: 4481326976 # append  Is added to the original list 
In [32]: a_list.extend([2])
In [33]: id(a_list)
Out[33]: 4481326976 # extend  Also added to the original list 

Datetime also has a Boolean value
This is a pit


In [34]: import datetime
In [35]: print('"datetime.time(0,0,0)" (Midnight) ->', bool(datetime.time(0,0,0)))
('"datetime.time(0,0,0)" (Midnight) ->', False)
In [36]: print('"datetime.time(1,0,0)" (1 am) ->', bool(datetime.time(1,0,0)))
('"datetime.time(1,0,0)" (1 am) ->', True)


The difference between '==' and is
My understanding is that "is" is to judge the identity of two objects, and == is to judge the value of two objects


In [37]: a = 1
In [38]: b = 1
In [39]: print('a is b', bool(a is b))
('a is b', True)
In [40]: c = 999
In [41]: d = 999
In [42]: print('c is d', bool(c is d))
('c is d', False) #  The reason is that python Memory management , The cache -5 - 256 The object of 
In [43]: print('256 is 257-1', 256 is 257-1)
('256 is 257-1', True)
In [44]: print('257 is 258-1', 257 is 258 - 1)
('257 is 258-1', False)
In [45]: print('-5 is -6+1', -5 is -6+1)
('-5 is -6+1', True)
In [46]: print('-7 is -6-1', -7 is -6-1)
('-7 is -6-1', False)
In [47]: a = 'hello world!'
In [48]: b = 'hello world!'
In [49]: print('a is b,', a is b)
('a is b,', False) #  obviously   They are not cached , This is a 2 An object with a string of fields 
In [50]: print('a == b,', a == b)
('a == b,', True) #  But they have the same value 
# But,  There is a special case 
In [51]: a = float('nan')
In [52]: print('a is a,', a is a)
('a is a,', True)
In [53]: print('a == a,', a == a)
('a == a,', False) #  It's blinding me ~


Light copy and deep copy
In practice, we can make changes to a list of objects, but we may not want to change the original list


In [65]: list1 = [1, 2]
In [66]: list2 = list1 #  It's a quote ,  Your operation list2, Actually, list1 The results will also change 
In [67]: list3 = list1[:]
In [69]: import copy
In [70]: list4 = copy.copy(list1) #  He and list3 The same   It's all shallow copy 
In [71]: id(list1), id(list2), id(list3), id(list4)
Out[71]: (4480620232, 4480620232, 4479667880, 4494894720)
In [72]: list2[0] = 3
In [73]: print('list1:', list1)
('list1:', [3, 2])
In [74]: list3[0] = 4
In [75]: list4[1] = 4
In [76]: print('list1:', list1)
('list1:', [3, 2]) #  right list3 and list4 None of the operations are correct list1 Have an impact on 
#  Now look at the difference between a deep copy and a shallow copy 
In [88]: from copy import copy, deepcopy
In [89]: list1 = [[1], [2]]
In [90]: list2 = copy(list1) #  Shallow copy 
In [91]: list3 = deepcopy(list1) #  Deep copy 
In [92]: id(list1), id(list2), id(list3)
Out[92]: (4494896592, 4495349160, 4494896088)
In [93]: list2[0][0] = 3
In [94]: print('list1:', list1)
('list1:', [[3], [2]]) #  see   Suppose you manipulate child objects   It's the same as the quote   Affected the source 
In [95]: list3[0][0] = 5
In [96]: print('list1:', list1)
('list1:', [[3], [2]]) #  Deep copy doesn't matter 

A bool is actually a subclass of int


In [97]: isinstance(True, int)
Out[97]: True
In [98]: True + True
Out[98]: 2
In [99]: 3 * True + True
Out[99]: 4
In [100]: 3 * True - False
Out[100]: 3
In [104]: True << 10
Out[104]: 1024

Is tuple really immutable?

In [111]: tup = ([],)
In [112]: tup[0] += [1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-112-d4f292cf35de> in <module>()
----> 1 tup[0] += [1]
TypeError: 'tuple' object does not support item assignment
In [113]: tup
Out[113]: ([1],) #  Shit   It's blinding me again , Obviously threw an exception   Can also be modified ?
In [114]: tup = ([],)
In [115]: tup[0].extend([1])
In [116]: tup[0]
Out[116]: [1] #  Okay, , I kind of see it ,  Although I cannot manipulate tuples directly, I cannot prevent myself from manipulating mutable children of tuples (list)

Here's a good explanation: Python's += Is Weird, Part II:


In [117]: my_tup = (1,)
In [118]: my_tup += (4,)
In [119]: my_tup = my_tup + (5,)
In [120]: my_tup
Out[120]: (1, 4, 5) # ?  well   You can't manipulate tuples ?
In [121]: my_tup = (1,)
In [122]: print(id(my_tup))
4481317904
In [123]: my_tup += (4,)
In [124]: print(id(my_tup))
4480606864 #  Not the original tuple   So you can 
In [125]: my_tup = my_tup + (5,)
In [126]: print(id(my_tup))
4474234912

16. Python has no private methods/variables? But there can be "fake" ones


In [127]: class my_class(object^E):
   .....:     def public_method(self):
   .....:         print('Hello public world!')
   .....:     def __private_method(self): #  Private starts with a double underscore 
   .....:         print('Hello private world!')
   .....:     def call_private_method_in_class(self):
   .....:         self.__private_method()
In [132]: my_instance = my_class()
In [133]: my_instance.public_method()
Hello public world! #  Common methods 
In [134]: my_instance._my_class__private_method()
Hello private world! #  Private ones can be added "_ +  The class name  +  Private method name" 
In [135]: my_instance.call_private_method_in_class()
Hello private world! #  It can also be accessed internally through the public interface provided by the class 
In [136]: my_instance._my_class__private_variable
Out[136]: 1

Exception handling plus else


In [150]: try:
   .....:     print('third element:', a_list[2])
   .....: except IndexError:
   .....:     print('raised IndexError')
   .....: else:
   .....:     print('no error in try-block') #  Only in the try It only executes when there are no exceptions in it else The inside expression 
   .....:
raised IndexError #  The thrown exception   Not quite done 
In [153]: i = 0
In [154]: while i < 2:
   .....:     print(i)
   .....:     i += 1
   .....: else:
   .....:     print('in else')
   .....:
0
1
in else # while Also support oh ~
In [155]: i = 0
In [156]: while i < 2:
   .....:         print(i)
   .....:         i += 1
   .....:         break
   .....: else:
   .....:         print('completed while-loop')
   .....:
0 #  be break the   Not fully executed   Don't perform else Inside of the 
In [158]: for i in range(2):
   .....:         print(i)
   .....: else:
   .....:         print('completed for-loop')
   .....:
0
1
completed for-loop
In [159]: for i in range(2):
   .....:         print(i)
   .....:         break
   .....: else:
   .....:         print('completed for-loop')
   .....:
0 #  It is also because break the 


Related articles: