Summary of Python rare knowledge points

Summary of Python rare knowledge points

This article borrows a lot from  https://github.com/leisurelicht/wtfpython-cn

□ except ... finally: All logic in finally will be executed regardless of whether the entire program is executed normally or not. At the same time, the return in finally will overwrite the execution result of the previous logic, and even exceptions will be ignored. Therefore, it is recommended to only add resource-related logic in finally

def calculate(division):
    try:
        return 100 / division
    except ZeroDivisionError as e:
        raise ValueError("Invalid inputs")
    finally:
        return 0
    
print(calculate(0))

'''
最后不会报错,而是正常退出返回 0
'''

□ for ... in: The for ... in operation will first call __iter__ to get an iterator, and then call __next__ to access the elements in turn. Therefore, if the content of the element is modified during the iteration process, because __next__ accesses the element according to the address bit, an abnormal situation will occur.

There is another knowledge point here: iterable iterable contains iterator, and iterator contains generator. At the same time, as long as it can be looped through for, it is iterable, and iterator can access elements in turn through next. This is the only useful way to traverse iterators. Let's briefly explain with a picture.

list_1 = [1, 2, 3, 4]
list_2 = [1, 2, 3, 4]
list_3 = [1, 2, 3, 4]
list_4 = [1, 2, 3, 4]

for idx, item in enumerate(list_1):
    del item

for idx, item in enumerate(list_2):
    list_2.remove(item)

for idx, item in enumerate(list_3[:]):
    list_3.remove(item)

for idx, item in enumerate(list_4):
    list_4.pop(idx)

Output:

>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]

'''
del, remove 和 pop 的不同: 

del var_name 只是从本地或全局命名空间中删除了 var_name (这就是为什么 list_1 没有受到影响). 

remove 会删除第一个匹配到的指定值, 而不是特定的索引, 如果找不到值则抛出 ValueError 异常. 

pop 则会删除指定索引处的元素并返回它, 如果指定了无效的索引则抛出 IndexError 异常.
'''
for i in range(4):
    print(i)
    i = 10 

# 在每次迭代开始之前, 迭代器(这里指 range(4)) 生成的下一个元素就被解包并赋值给目标列表的变量(这里指 i)了.
'''
output:
0
1
2
3
'''

□ python =: The assignment operation in python is just a simple reference assignment for a variable object, and the two references actually point to the same memory object.

list1 = [1, 2, 3]
list2 = list1
list2[0] = 6

'''
list1 = [6, 2, 3]
list2 = [6, 2, 3]
'''

□ List is empty: It is recommended to use list and not list directly to judge whether the list is not empty or empty in PEP 8.

□ Use variable objects as function default parameters: The default value of the parameter is already set when the method definition is executed, which means that the default value will only be set once. After the function is defined, it will be set every time it is called There is a "pre-calculation" process. At this point, if the variable object is modified inside the function, then the default input afterwards will be the modified object

#!/usr/bin/env python
# coding: utf-8

class Test(Object):
    def process_data(self, data=[]):
        # 排序
        data.sort()
        # 追加结束符
        data.append("End")
        return data

test1 = Test()
print(test1.process_data())

test2 = Test()
print(test2.process_data())

test3 = Test()
print(test3.process_data(data=["Name:123", "Age:34"]))

test4 = Test()
print(test4.process_data())

'''
输出:
['End']
['End', 'End']
["Age:34", "Name:123", 'End']
['End', 'End', 'End']
'''

If you really need to default to a variable object, you can use the following method

def some_func(default_arg=None):
    if default_arg is None:
        default_arg = []
    default_arg.append("some_string")
    return default_arg

 □ nonlocal: nonlocal can only get local variables at one level outside the current scope, and its expansion level can only be 1

n = 0

def f():
    def inner():
        nonlocal n
        n = 2
    
    n = 1
    print(n)
    inner()
    print(n)

if __name__ == "__main__":
    f()
    print()

'''
输出:
1
2
0
'''

□ List slice: When list is sliced, stop should be on the logical right side of start, otherwise the output is empty

TempStr = 'Hello World'

print(TempStr[-5:-1])
print(TempStr[-5:0])
print(TempStr[-4:-1])
print(TempStr[-5:])

'''
输出:
Worl

orl
World
'''

□ __all__: __all__ can only limit the objects exported by from ... import *. At this time, only the objects specified in __all__ can be exported. However, all objects can still be explicitly exported through from ... import ....

□ Closure value transfer: The value passed through the closure is passed through the object address, so if the incoming value is modified externally, the result inside the closure will also be affected. However, if the incoming value is directly modified internally, the incoming value of the closure in the local scope will be invalid, and it may prompt that it is not copied. At this time, if you use nonlocal to specify the value of the closure, you can modify the external variables inside the closure.

#!/usr/bin/env python
# coding: utf-8

def a(s):
    def b():
        print(i)
        # i = 2  # 添加该行,由于指定了局部变量,对应闭包传递过来的对象引用被覆盖,将调试在赋值前引用
        # print(i)
    i = s
    print(i)
    return b


if __name__ == '__main__':
    tb1 = a(1)
    tb1()
    tb2 = a(2)
    tb1()
    tb2()

'''
输出:
1
1
2
1
2
'''

□ Closure value transfer: When defining a function inside a loop, if the function uses a loop variable in its body, the closure function will be bound to the loop variable, not its value. Therefore, all functions are It is calculated using the last value assigned to the variable. 

def f():
    t = [lambda x: i*x for i in range(4)]
    return t
    
print([M(2) for M in f()])

'''
output:
[6, 6, 6, 6]
'''
'''
这个例子本质不是闭包值传递,而是正常的函数值传递
而由于每次都会创建一个局部变量,不同函数对应的局部变量的内存必然不同,因此不存在影响
'''
funcs = []
for x in range(7):
    def some_func(x=x):
        return x
    funcs.append(some_func)

Output:

>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]

□ The timing of execution of the in clause: In the generator expression, the in clause is executed at the time of declaration, while the conditional clause is executed at runtime

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]

print(list(g))

"""
output:
[8]
"""

array_1 = [1,2,3,4]
g1 = (x for x in array_1)
array_1 = [1,2,3,4,5]

print(list(g1))

array_2 = [1,2,3,4]
g2 = (x for x in array_2)
array_2[:] = [1,2,3,4,5]

print(list(g2))

"""
output:
[1,2,3,4]
[1,2,3,4,5]
"""

□ Multiplication of list objects: For variable objects in the list, use multiplication to copy, and all copied variable objects will point to the same memory space

# 我们先初始化一个变量row
row = [""]*3 #row i['', '', '']
# 并创建一个变量board
board = [row]*3

>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]

□ is not: is not is a single binary operator, which is different from using is and not separately. If the variables on both sides of the operator point to the same object, the result of is not is False, otherwise the result is True.

□ Backslash string identified by r: What the interpreter does is simply change the behavior of the backslash, so it will directly pass the backslash and the next character. At this time, if the string ends with a backslash, an error will be reported SyntaxError: EOL while scanning string literal

□ bool and int: Boolean values ​​are subtypes of int, so the integer value of True is 1, and the integer value of False is 0

□ Class attributes and instance attributes: internally, class variables and instance variables are handled through the dictionary of the class object (that is, the __dict__ attribute). If you can't find it in the dictionary of the current class, then go to its parent class. The += operator modifies mutable objects in place, rather than creating new ones. Therefore, in this case, modifying the properties of one instance will affect other instance and class properties.

class A:
    x = 1

class B(A):
    pass

class C(A):
    pass

Output:

>>> A.x, B.x, C.x
(1, 1, 1)
>>> B.x = 2
>>> A.x, B.x, C.x
(1, 2, 1)
>>> A.x = 3
>>> A.x, B.x, C.x
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)


class SomeClass:
    some_var = 15
    some_list = [5]
    another_list = [5]
    def __init__(self, x):
        self.some_var = x + 1
        self.some_list = self.some_list + [x]
        self.another_list += [x]

Output:

>>> some_obj = SomeClass(420)
>>> some_obj.some_list
[5, 420]
>>> some_obj.another_list
[5, 420]
>>> another_obj = SomeClass(111)
>>> another_obj.some_list
[5, 111]
>>> another_obj.another_list
[5, 420, 111]
>>> another_obj.another_list is SomeClass.another_list
True
>>> another_obj.another_list is some_obj.another_list
True
class Base:
    inited = False
    
    @classmethod
    def set_inited(cls): # 实际可能传入Derived类
        cls.inited = True # 并没有修改Base.inited,而是给Derived添加了成员

class Derived(Base):
    pass

x = Derived()
x.set_inited()
if Base.inited:
    print("Base is inited") # 不会被执行

□ += operator: the += operator modifies the list in place. The element assignment operation does not work, but when an exception is thrown, the element has been modified in place

'''
对于不可变对象, 这里指tuple, += 并不是原子操作, 而是 extend 和 = 两个动作
这里 = 操作虽然会抛出异常, 但 extend 操作已经修改成功了
'''
some_tuple = ("A", "tuple", "with", "values")
another_tuple = ([1, 2], [3, 4], [5, 6])

>>> some_tuple[2] = "change this"
TypeError: 'tuple' object does not support item assignment
>>> another_tuple[2].append(1000) # 这里不出现错误
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000])
>>> another_tuple[2] += [99, 999]
TypeError: 'tuple' object does not support item assignment
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000, 99, 999])

□ numpy.empty(): numpy.empty() directly returns to the next free memory without reinitialization.

import numpy as np

def energy_send(x):
    # 初始化一个 numpy 数组
    np.array([float(x)])

def energy_receive():
    # 返回一个空的 numpy 数组
    return np.empty((), dtype=np.float).tolist()
Output:

>>> energy_send(123.456)
>>> energy_receive()
123.456

□ Dictionary modification in the iterative process: Try not to modify the iterative object during the iterative process, as the result is often unpredictable.

x = {0: None}

for i in x:
    del x[i]
    x[i+1] = None
    print(i)

Output (Python 3.7):
# 这个结果好像跟字典的自动扩容有关,扩容会导致散列表地址发生变化而中断循环
0
1
2
3
4

□ Generator expression and list recursive variable scope: The scope nested in the class definition will ignore the name binding within the class. The generator expression has its own scope. Starting from Python 3.X, list comprehensions also have their own scope.

# 这次我们先初始化x
x = -1
for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

Output:

6 : for x inside loop
6 : x in global


x = 1
print([x for x in range(5)])
print(x, ': x in global')

Output (on Python 2.x):

[0, 1, 2, 3, 4]
(4, ': x in global')

# python 3 的列表推导式不再泄露
Output (on Python 3.x): 

[0, 1, 2, 3, 4]
1 : x in global
x = 5
class SomeClass:
    x = 17
    y = (x for i in range(10))

Output:

>>> list(SomeClass.y)[0]
5


x = 5
class SomeClass:
    x = 17
    y = [x for i in range(10)]

Output (Python 2.x):

>>> SomeClass.y[0]
17

Output (Python 3.x):

>>> SomeClass.y[0]
5

□ else: The else clause after the loop will only be executed when the loop does not trigger the break statement and ends normally. The else clause after try is also called "completion clause", because reaching the else clause in the try statement means that the try block has actually been successfully completed.

  def does_exists_num(l, to_find):
      for num in l:
          if num == to_find:
              print("Exists!")
              break
      else:
          print("Does not exist")

Output:

>>> some_list = [1, 2, 3, 4, 5]
>>> does_exists_num(some_list, 4)
Exists!
>>> does_exists_num(some_list, -1)
Does not exist


try:
    pass
except:
    print("Exception occurred!!!")
else:
    print("Try block executed successfully...")

Output:

Try block executed successfully...

□ Double underscore private variables: In Python, the interpreter  modifies (mangles) the names __ of class members that start with  (double underscore) and end with at most one underscore_NameOfTheClass

class Yo(object):
    def __init__(self):
        self.__honey = True
        self.bitch = True

Output:

>>> Yo().bitch
True
>>> Yo().__honey
AttributeError: 'Yo' object has no attribute '__honey'
>>> Yo()._Yo__honey
True

 

Guess you like

Origin blog.csdn.net/a40850273/article/details/106232621