Deep copy and shallow copy
Shallow copy is to copy only the first layer of the object. If there is a variable type reference in the first layer, if the variable type changes, the copied object will also change. For example
a = [1, 2, [1,2]] b = copy.copy(a) # Here, the final copy of both copy and list generated for loop slices is a shallow copy. The fundamental principle is that what is contained in the list is not the real value of the element, but the reference address of the element because [1,2]Is a variable type and b The reference address of the list is stored in it, so when the list is changed a Will change with it
Deep copy is copying layer by layer until there is no mutable type
a = [1,2] b = [1,2,a] c = copy.deepcopy(b) This is equivalent to a Copy the contents layer by layer and put them into a new memory address
Iteratable object, iterator, generator
Iteratable object:
That is, there are two types of objects that can be directly used in the for loop:
1. Set data type: list, tuple, str, set, dict
2. Iterator, generator, or generator function containing yield
Iterator:
Iterators are iteratable objects
The object that can be called by the next() function and continuously return the next value is called iterator: iteratable
Because the python built-in method next() essentially calls the iterator itself__ next__ () method
So the iterator needs to implement the next() method, and then the iterator itself is an iteratable object
So we need to implement the iter() method
Why? Because an iterator of the for loop will call python's built-in iter() method first
It is also the essence of calling iterator ()__ iter__ () method
Simple iterator code
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # Initialize two counters a, b def __iter__(self): return self # The for loop calls the iter() method, which in turn calls its own__ iter__ Method returns an iterator. Because it is an iterator, it is OK to return itself def __next__(self):# ditto self.a, self.b = self.b, self.a + self.b # Calculate next value if self.a > 100000: # Conditions for exiting the loop raise StopIteration(); return self.a # Returns the next value
Generator:
The generator belongs to an iterator
There are two types of generators:
first kind
Generator function: still use def to define the function, but use yield instead of return statement to return the result. The yield statement returns one result at a time. In the middle of each result, suspend the state of the function so that execution can continue from where it left next time.
def foo(num): print("starting...") while num<10: num=num+1 yield num for n in foo(0): # The for loop will continuously call the next() method to continue to run in the single place in the yield print(n)
Category II:
Generator expression: similar to list derivation, it just transforms a pair of braces [] into a pair of parentheses (). However, the generator expression generates a generator result object on demand. If you want to get each element, you need to cycle through it.
# A list xiaoke=[2,3,4,5] # Generator generator, similar to list, but change [] to () gen=(a for a in xiaoke) for i in gen: print(i) #The result is: 2 3 4 5
The objects of the for loop are all iteratable objects, but it needs to get an iterator, and then call the iterator through the next() method to get the next value of the object
So it calls the python built-in function iter() method
If the iteratable object is a container object such as list and dict, iter() can return an iterator
If the iteratable object is an iterator, iter() will call the iterator itself__ iter__ Method, and the next() method called by subsequent iterations also calls the iterator itself__ next__ () method
So an iterator needs to have__ iter__ Method this method returns itself__ next__ method
The iteratable objects list, tuple and dict only have__ iter__ Method will return an iterator (not itself)__ next__ method
Summary:
1. Iterators are iteratable objects, but iteratable objects are not necessarily iterators
2. Generators are iterators, but iterators are not necessarily generators
3. The iteratable data types list, str, dict, etc. are iteratable, but they are not iterators. However, an Iterator object can be obtained through the iter(list) function
Closure, decorator
closure
Characteristics of closures:
1. An internal function is defined in the external function
2. The external function has a return value
3. The returned value is: internal function name
4. The internal function also refers to the variable value of the external function
Generally, if a function ends, everything inside the function will be released and returned to memory, and the local variables will disappear. However, closure is a special case. If the external function finds that it has its own temporary variable that will be used in the internal function in the future, it binds the temporary variable to the internal function, and then ends it by itself.
- Closure with parameters
#Closure with parameters def func2(a,b):#Two input parameters a,b c=100 #External function parameters def inner_func2():#Internal function d=3 #Arguments to internal functions sum=a+b+c+d #Parameters that reference external functions print(sum) return inner_func2 #Returns the internal function name f=func2(1,6)#Funcf inner return 2_ func2 f() #Equivalent to calling inner_func2
nonlocal variable name # internal function can modify the immutable variable of external function
Global variable name # internal function can modify global immutable variable
Decorators are a use of closures
Decorator
- Decorator function
If the decorator does not need parameters, the first layer is not required
#Decorator with parameters def outer(q):#The first layer is responsible for receiving the parameters of the decorator def decorate(func):#The second layer is responsible for receiving the decorated function def inner_func(*args,**kwargs):#The third layer is responsible for receiving the parameters of the decorated function func(*args,**kwargs) print('Lay the floor{}block'.format(q)) return inner_func#Come back to the third floor return decorate#Come back to the second floor @outer(q=10)#Decorator belt parameter q=10 def house(time): print('I'm a blank room,{}Start decoration'.format(time)) @outer(q=100)#Decorator belt parameter q=100 def street(): print('The name of the new road is Shennan Avenue') house('2019-12-21') street()
- Class decorator
Similarly, decorators with parameters need three layers. One layer is responsible for receiving the parameters of the decorator, one layer is responsible for receiving the decorated function, and the other layer is responsible for receiving the parameters of the decorated function
class BaiyuDecorator: def __init__(self, arg1, arg2): # Equivalent to the first layer in the decorator function, which is used to receive the parameters of the decorator print('Execution class Decorator of__init__()method') self.arg1 = arg1 self.arg2 = arg2 def __call__(self, func): # Equivalent to the second layer used to receive the decorated function print('Execution class Decorator of__call__()method') def baiyu_warp(*args): # Equivalent to the third layer used to receive the parameters of the decorated function print('implement wrap()') print('Decorator parameters:', self.arg1, self.arg2) print('implement' + func.__name__ + '()') func(*args) print(func.__name__ + '()completion of enforcement') return baiyu_warp @BaiyuDecorator('Parameter 1', 'Parameter 2') # 1. The class decorator will be__ init__ Method go through def example(a1, a2, a3): # 2. When executed here, the class decorator__ call__ Method go through print('afferent example()Parameters:', a1, a2, a3) if __name__ == '__main__': print('Ready to call example()') example('Baiyu', 'Happy', 'Coder') # 3. When running here, run the decorator first__ call__ Baiyu in the method_ Warp decorator print('Test code execution completed')
If the class decorator does not need parameters, it only needs two layers
- Decorator without parameters
class BaiyuDecorator: def __init__(self, func): # The decorator does not need parameters, so the original first layer is directly used to install the decorated function self.func = func print(".Execution class__init__method") def __call__(self, *args, **kwargs): # The original second layer is used to hold the parameters of the decorated function print('get into__call__function') t1 = time.time() self.func(*args, **kwargs) print("Execution time:", time.time() - t1) @BaiyuDecorator def baiyu(): # The class decorator will not run until it runs here__ init__ Method and pass the address of the method to the parameter print("I am the white jade of the siege lion") time.sleep(2) if __name__ == '__main__': # print(blog) pass print('1111') baiyu()