python basic interview questions

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()  

Keywords: Python Interview

Added by nilesh on Fri, 11 Feb 2022 23:31:10 +0200