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