Question 1

What exactly is Python? You can compare your answers to other techniques (and this is encouraged).

The answer

Here are some key points:

  • Python is an interpreted language. This means that, unlike C and its derivatives, Python code does not need to be compiled before it can be run. Other interpreted languages include PHP and Ruby.
  • Python is dynamically typed, which means you don’t have to specify the type of a variable when you declare it. You can just write code like x=111 and x=”I’m a string” and it won’t give an error.
  • Python is well suited for OOP because it allows classes to be defined through composition and inheritance. There is no access specifier (similar to public and private in C++) in Python, which is designed on the basis that “everyone is an adult”.
  • In Python, functions are first-class objects. This means that they can be assigned to variables, and functions can either return function types or accept functions as input. Classes are also objects of the first class.
  • Python code is fast to write, but typically runs slower than compiled languages. Fortunately, Python allows extensions written in C, so we can optimize the code and eliminate bottlenecks, which is usually possible. Numpy is a good example, and it’s really fast, because a lot of arithmetic isn’t actually done in Python.
  • Python is used for a wide variety of purposes — networking applications, automation, scientific modeling, big data applications, and so on. It is also often used as a “glue language” to help improve the health of other languages and components.
  • Python makes hard things easy, so programmers can focus on designing algorithms and data structures without dealing with the low-level details.

Why ask this question:

If you’re applying for a Python development position, you should know what the language is and why it’s so cool. And what’s wrong with it.

Question 2

Add missing code

Def print_directory_contents(sPath): """ This function takes the name of the folder as an input parameter and returns the path of the files in that folder, as well as the path of the files in its containing folder. "" # add codeCopy the code

The answer

Def print_directory_contents(sPath): import OS for sChild in os.listdir(sPath): sChildPath = os.path.join(sPath,sChild) if os.path.isdir(sChildPath): print_directory_contents(sChildPath) else: print sChildPathCopy the code

Special attention should be paid to the following points:

  • Naming conventions should be unified. If the naming conventions are visible in the sample code, follow the existing ones.
  • Recursive functions need to recurse and terminate. Make sure you understand how this works, or you will face an endless callstack.
  • We use OS module to interact with the operating system, and the way of interaction can be cross-platform. You can write the code as sChildPath = sPath + ‘/’ + sChild, but this will fail on Windows.
  • It’s very valuable to be familiar with the basic modules, but don’t try to memorize them all. Remember that Google is a great mentor for your work.
  • If you don’t understand what the code is supposed to do, don’t hesitate to ask.
  • Stick to the KISS principle! Keep it simple, but the brain gets it!

Why ask this question:

  • Demonstrate the applicant’s basic knowledge of interacting with operating systems
  • Recursion is so handy

Question 3

Read the code below and write down the final values for A0, A1, and An.

A0 = dict (zip ((' a ', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, 5))) A1 = range (10) A2 = [I for I A1 if I in A0] in A3 = [A0 [s] for s in A0] A4 = [i for i in A1 if i in A3] A5 = {i:i*i for i in A1} A6 = [[i,i*i] for i in A1]Copy the code

The answer

A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
A2 = []
A3 = [1, 3, 2, 5, 4]
A4 = [1, 2, 3, 4, 5]
A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]
Copy the code

Why ask this question:

  • List Comprehension is a great time saver and a major learning impediment for many people.
  • If you can read the code, you’ll probably be able to write down the correct values.
  • Some of the code is purposely weird. Because some of the people you work with will be weirdos.

Question 4

Python and multi-threading. Is that a good idea? List some ways to make Python code run in parallel.

The answer

Python does not support true multithreading. Multithreaded packages are available in Python, but using multithreaded packages is not a good idea if you want to speed up your code. Python has something called the Global Interpreter Lock (GIL), which ensures that only one of your multiple threads is being executed at any one time. Threads execute so fast that you might think they’re executing in parallel, but they’re actually executing in turns. Going through the GIL level increases the overhead of execution. This means that if you want to speed up your code, using the Threading package is not a good idea.

However, there are many reasons to use the threading package. This package is perfectly fine and convenient if you want to perform several tasks at the same time without worrying about efficiency. But most of the time, this isn’t the case. You’ll want to outsource the multithreading to the operating system (by starting multiple processes), or to some external program that calls your Python code (such as Spark or Hadoop), or to some other code that your Python code calls (for example, You can call C functions in Python to handle expensive multithreading.

Why do you ask

Because GIL is an a-hole. Many people spend a lot of time trying to find bottlenecks in their multithreaded code until they understand that GIL exists.

Question 5

How do you manage different versions of code?

The answer:

Versioning! Be excited when asked, and even tell them how you use Git (or your favorite tool) to track your correspondence with grandma. I prefer to use Git as a version control system (VCS), but there are other options, such as Subversion (SVN).

Why ask this question:

Because without version control code, it’s like coffee without cups. Sometimes we need to write one-off scripts that can be thrown away, in which case it’s ok not to do version control. But if you’re dealing with a lot of code, using version control can be advantageous. Version control helps you keep track of who is doing what to your code base; Discover what bugs have been introduced. Manage the different versions and distributions of your software; Sharing source code among team members; Deployment and other automation. It lets you roll back to the version before the problem, which is great alone. There are other nice features as well. What a great word!

Question 6

The following code outputs what:

Def f(x,l=[]): for I in range(x): I * I print f(2) f(3,[3,2,1]) f(3) def f(x,l=[]): for I in range(x): I * I print f(3, 2,1) f(3)Copy the code

The answer:

[0, 1] [3, 2, 1, 0, 1, 4]Copy the code

Eh?

The first function call is obvious. The for loop adds 0 and 1 to the empty list L. L is the name of the variable that points to a list stored in memory. The second function call creates a new list in a new block of memory. L now points to the newly generated list. Then add 0, 1, 2, and 4 to the new list. Great. The result of the third function call is a bit strange. It uses the old list stored in the previous memory address. That’s why its first two elements are 0 and 1.

If you don’t understand, try the following code:

l_mem = [] l = l_mem # the first call for i in range(2): L.apend (I * I) print l # [0, 1] l = [3,2,1] # the second call for I in range(3): l.append(i*i) print l # [3, 2, 1, 0, 1, 4] l = l_mem # the third call for i in range(3): l.append(i*i) print l # [0, 1, 0, 1, 4]Copy the code

Question 7

What is “monkey patching”? Is that a good idea?

The answer:

Monkey patching refers to changing the behavior of functions or objects after they have been defined.

Here’s an example:

import datetime
datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12)
Copy the code

In most cases, this is a bad practice – because it is best that functions behave identically in the code base. The reason for the “monkey patch” is probably for testing. Mock packages are helpful for this purpose.

Why do you ask?

Getting this question right means you know something about unit testing methods. If you mention avoiding “monkey patches,” you’re not the kind of programmer who likes fancy code (there are people like that in the company, and they’re terrible to work with), and you’re more maintainable. Remember the KISS code? Getting this question right also shows that you know something about the underlying workings of Python, how functions are actually stored, called, and so on.

Also: If you haven’t read mock modules, it’s worth taking the time to read them. This module is very useful.

Question 8

What do these two parameters mean: *args, **kwargs? Why do we use them?

The answer

If we are not sure how many arguments to pass to a function, or if we want to pass arguments to a function as lists and tuples, use *args; We use **kwargs if we don’t know how many keyword arguments to pass into a function, or if we want to pass dictionary values as keyword arguments. Args and kwargs are conventional identifiers. Of course you could use * Bob and ** Billy, but that’s not a good idea.

Here is a concrete example:

Def f(*args,**kwargs): Print the args, kwargs l = [1, 2, 3] t = (4 and 6) d = {' a ': 7,' b ': 8,' c ': 9} f (f) (1, 2, 3) # (1, 2, 3) {} f (1, 2, 3, "groovy") # (1, 2, 3, 'groovy') {} f(a=1,b=2,c=3) # () {'a': 1, 'c': 3, 'b': 2} f(a=1,b=2,c=3,zzz="hi") # () {'a': 1, 'c': 3, 'b': 2, 'z' : 'hi'} f (1, 2, 3, a = 1, b = 2, c = 3) # (1, 2, 3) {' a ': 1, "c" : 3,' b ': 2} f (* * * l, d) # (1, 2, 3) {' a' : 7, 'c' : 9, 'b' : 8 (* * * t, d)} f # (4, 5, 6) {' a ': 7,' c ': 9,' b ': 8} f (t) 1, 2, * # (1, 2, 4, 5, 6) {} f (q = "winning", * * d) # () {' a' : 7, 'q' : 'winning', 'c' : 9, 'b' : 8} f (1, 2, * t, q = "winning", * * d) # (1, 2, 4, 5, 6) {' a ': 7,' q ':' winning ', 'c' : 9, 'b' : 8} def f2(arg1,arg2,*args,**kwargs): Print arg1,arg2, args, kwargs f2(1,2,3) # 2(2,3,"groovy") {} 'groovy') {} f2(arg1=1,arg2=2,c=3) # 1 2 () {'c': 3} f2(arg1=1,arg2=2,c=3,zzz="hi") # 1 2 () {'c': 3, 'zzz': 'hi'} f2 (1, 2, 3, a = 1, b = 2, c = 3) # 1, 2 (3) {' a ': 1, "c" : 3,' b ': 2} f2 (* * * l, d) # 1, 2 (3) {' a' : 7, 'c' : 9, 'b' : 8} f2 (* * * t, d) # 4, 5, 6,) {' a ': 7,' c ': 9,' b ': 8} f2 (1, 2, * t) # 1, 2, 4, 5, 6) {} f2 (1, 1, q = "winning", * * d) # 1 1 () {' a' : 7, 'q' : 'winning', 'c' : 9, 'b' : 8} f2 (1, 2, * t, q = "winning", * * d) # 1, 2, 4, 5, 6) {' a ': 7,' q ':' winning ', 'c' : 9, 'b' : 8}Copy the code

Why do you ask?

Sometimes we need to pass an unknown number of arguments or keyword arguments into a function. Sometimes we also want to store parameters or keyword parameters for later use. Sometimes, it’s just to save time.

Question 9

@classMethod, @staticMethod, @property?

Answer background information

These are all decorators. A decorator is a special type of function that either takes a function as an input parameter and returns a function, or a class as an input parameter and returns a class. The @ tag is syntactic sugar, which allows you to decorate target objects in an easy-to-read way.

Decorator def my_func(stuff): do_things Is equivalent to def my_func(stuff): do_things my_func = my_decorator(my_func)Copy the code

You can find a textbook on how decorators work on this website.

The real answer

The objects used by the @classMethod, @StaticMethod, and @Property decorators are functions defined in the class. The following example shows their usage and behavior:

class MyClass(object): def __init__(self): self._some_property = "properties are nice" self._some_other_property = "VERY nice" def normal_method(*args,**kwargs): print "calling normal_method({0},{1})".format(args,kwargs) @classmethod def class_method(*args,**kwargs): print "calling class_method({0},{1})".format(args,kwargs) @staticmethod def static_method(*args,**kwargs): print "calling static_method({0},{1})".format(args,kwargs) @property def some_property(self,*args,**kwargs): print "calling some_property getter({0},{1},{2})".format(self,args,kwargs) return self._some_property @some_property.setter def some_property(self,*args,**kwargs): print "calling some_property setter({0},{1},{2})".format(self,args,kwargs) self._some_property = args[0] @property def some_other_property(self,*args,**kwargs): print "calling some_other_property getter({0},{1},{2})".format(self,args,kwargs) return self._some_other_property o = MyClass() # Undecorated methods still behave normally and require the current class instance (self) as the first argument. o.normal_method # <bound method MyClass.normal_method of <__main__.MyClass instance at 0x7fdd2537ea28>> Normal_method ((<__main__.MyClass instance at 0x7fdd2537ea28>,),{}). Normal_method (1,2,x=3,y=4) # normal_method((<__main__.MyClass instance at 0x7fdd2537ea28>, 1, 2),{'y': 4, 'x': # <bound method classobj.class_method of <class __main__.MyClass at 0x7FDD2536a390 >> O.class_method () # class_method((<class __main__.myclass at 0x7fdd2536a390>,),{}) o.class_method(1,2,x=3,y=4) # class_method((<class __main__.MyClass at 0x7fdd2536a390>, 1, 2),{'y': 4, 'x': 3}) # Static methods have no arguments other than those passed in when you call them. o.static_method # <function static_method at 0x7fdd25375848> o.static_method() # static_method((),{}) O.set_method (1,2,x=3,y=4) # static_method((1, 2),{'y': 4, 'x': 3}) It is an error to call them directly. The "read-only" property can be implemented by defining only getter methods, not setter methods. O.home_property # Call the getter for some_property (<__main__.MyClass instance at 0x7fb2b70877E8 >,(),{}) # 'properties are nice' # O.home_property () # calling some_property getter(<__main__.MyClass instance at 0x7Fb2b70877E8 >,(),{}) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: 'str' object is not callable o.some_other_property # calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # 'VERY nice' # o.some_other_property() # calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: 'str' object is not callable o.some_property = "groovy" # calling some_property setter(<__main__.MyClass object at 0x7fb2b7077890>,('groovy',),{}) o.some_property # calling some_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{}) # 'groovy' o.some_other_property = "very groovy" # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # AttributeError: can't set attribute o.some_other_property # calling some_other_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{})Copy the code

Question 10

Read the code below. What is the output?

class A(object): def go(self): print "go A go!" def stop(self): print "stop A stop!" def pause(self): raise Exception("Not Implemented") class B(A): def go(self): super(B, self).go() print "go B go!" class C(A): def go(self): super(C, self).go() print "go C go!" def stop(self): super(C, self).stop() print "stop C stop!" class D(B,C): def go(self): super(D, self).go() print "go D go!" def stop(self): super(D, self).stop() print "stop D stop!" def pause(self): print "wait D wait!" class E(B,C): Pass a = a = b () () b c = c = d () () d e = e (#) shows that the output of the following code al-qeada o be sad o chtistina georgina rossetti.british poetess b.g o () () () the d.g o () o um participant () a.s c.s. top top b.s top () () ()  d.stop() e.stop() a.pause() b.pause() c.pause() d.pause() e.pause()Copy the code

The answer

The output is in the form of comments:

a.go() # go A go! b.go() # go A go! # go B go! c.go() # go A go! # go C go! d.go() # go A go! # go C go! # go B go! # go D go! e.go() # go A go! # go C go! # go B go! a.stop() # stop A stop! b.stop() # stop A stop! c.stop() # stop A stop! # stop C stop! d.stop() # stop A stop! # stop C stop! # stop D stop! e.stop() # stop A stop! a.pause() # ... Exception: Not Implemented b.pause() # ... Exception: Not Implemented c.pause() # ... Exception: Not Implemented d.pause() # wait D wait! e.pause() # ... Exception: Not ImplementedCopy the code

Why do you ask?

Because object-oriented programming is really, really important. Don’t cheat you. Answering this question correctly shows that you understand inheritance and the use of the super function in Python.

Question 11

Read the code below. What is the output?

class Node(object): def __init__(self,sName): self._lChildren = [] self.sName = sName def __repr__(self): return "<Node '{}'>".format(self.sName) def append(self,*args,**kwargs): self._lChildren.append(*args,**kwargs) def print_all_1(self): print self for oChild in self._lChildren: oChild.print_all_1() def print_all_2(self): def gen(o): lAll = [o,] while lAll: oNext = lAll.pop(0) lAll.extend(oNext._lChildren) yield oNext for oNode in gen(self): print oNode oRoot = Node("root") oChild1 = Node("child1") oChild2 = Node("child2") oChild3 = Node("child3") oChild4 = Node("child4") oChild5 = Node("child5") oChild6 = Node("child6") oChild7 = Node("child7") oChild8 = Node("child8") oChild9 = Node("child9") oChild10 = Node("child10") oRoot.append(oChild1) oRoot.append(oChild2) oRoot.append(oChild3) oChild1.append(oChild4) oChild1.append(oChild5) oChild2.append(oChild6) oChild4.append(oChild7) oChild3.append(oChild8) Ochild3.append (oChild9) ochild6.append (oChild10) #Copy the code

The answer

ORoot. Print_all_1 () prints the following:

<Node 'root'>
<Node 'child1'>
<Node 'child4'>
<Node 'child7'>
<Node 'child5'>
<Node 'child2'>
<Node 'child6'>
<Node 'child10'>
<Node 'child3'>
<Node 'child8'>
<Node 'child9'>
Copy the code

ORoot. Print_all_1 () prints the following:

<Node 'root'>
<Node 'child1'>
<Node 'child2'>
<Node 'child3'>
<Node 'child4'>
<Node 'child5'>
<Node 'child6'>
<Node 'child8'>
<Node 'child9'>
<Node 'child7'>
<Node 'child10'>
Copy the code

Why do you ask?

The essence of an object is composition and object construction. Objects need to be composed of components and initialized in some way. Recursion and the use of generators are also involved.

Generators are great data types. You can achieve similar functionality to print_all_2 simply by constructing a long list and then printing the contents of the list. Generators also have the benefit of not taking up a lot of memory.

It is also worth noting that print_all_1 traverses the tree in depth-first fashion, whereas print_all_2 is width-first. Sometimes, one traversal is more appropriate than the other. But it depends on your application.

Question 12

A brief description of Python’s garbage collection mechanism.

The answer

There’s a lot to be said here. You should mention the following key points:

  • Python stores the reference count for each object in memory. If the count becomes zero, the corresponding object becomes small, and the memory allocated to that object is freed up for other uses.
  • Occasionally, a reference cycle occurs. The garbage collector periodically looks for the loop and collects it. For example, suppose you have two objects, O1 and O2, and they matchO1. x == O2 and O2. x == o1These two conditions. If o1 and O2 have no other code reference, then they should not continue to exist. But they all have a reference count of 1.
  • Certain heuristics are used in Python to speed up garbage collection. For example, objects created later are more likely to be reclaimed. After objects are created, the garbage collector assigns the generation to which they belong. Each object is assigned a generation, and objects assigned to younger generations are processed first.

Question 13

Rank the following functions in order of execution efficiency. They all accept as input a list of numbers between 0 and 1. The list can be long. An example of an input list is as follows: [random.random() for I in range(100000)]. How do you justify your answer?

Def f1(lIn): sorted(lIn) l2 = [I for I in l1 if I <0.5] return [I * I for I in l2] def f2(lIn): L1 = [I for I in lIn if I <0.5] l2 = sorted(l1) return [I * I for I in l2] def f3(lIn): L1 = [I * I for I in lIn] l2 = sorted(L1) return [I for I in L1 if I <(0.5*0.5)]Copy the code

The answer

In descending order of execution efficiency: F2, F1, and F3. To prove this answer, you should know how to analyze the performance of your own code. There is a good program analysis package in Python that meets this need.

import cProfile
lIn = [random.random() for i in range(100000)]
cProfile.run('f1(lIn)')
cProfile.run('f2(lIn)')
cProfile.run('f3(lIn)')
Copy the code

To give you a complete explanation, here is the output of the above analysis code:

>>> cprofile.run ('f1(lIn)') 4 function calls in 0.045 seconds Ordered by: Standard name ncalls tottime perCall cumtime percall filename:lineno(function) 1 0.009 0.009 0.044 0.044 <stdin>:1(f1) 1 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof. 3 function calls in 0.024 seconds Ordered by: Standard name ncalls tottime perCall cumtime percall filename: Lineno (function) 1 0.008 0.008 0.023 0.023 <stdin>:1(f2) 1 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} >>> 3 function calls in 0.055 seconds Ordered by: Standard name ncalls tottime perCall cumtime percall filename: Lineno (function) 1 0.016 0.016 0.054 0.054 <stdin>:1(f3) 1 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 3 {sorted}Copy the code

Why do you ask?

Locating and avoiding code bottlenecks is a valuable skill. Writing a lot of efficient code ultimately comes down to common sense — in the example above, it’s obviously faster to sort first if the list is small, so if you can filter before sorting, that’s usually a good idea. Other problems that are not obvious can still be located with the right tools. So it’s good to know these tools.

Question 14

Have you ever failed?

Wrong answer

I’ve never failed!

Why do you ask?

Answering this question properly shows that you are ready to admit your mistakes, take responsibility for them, and learn from them. All of these are especially important if you want to be helpful to others. Too bad if you’re a perfect person, you might even get a little creative when answering this question.

Question 15

Have you ever undertaken any personal projects?

A: really?

If you have done personal projects, it shows that you are willing to go beyond the minimum required in terms of updating your skill level. If you maintain personal projects and keep coding outside of work, your employer is more likely to see you as an asset that will grow in value. Even if they don’t ask, I think it’s helpful to talk about it.

conclusion

When I present these questions, I intentionally cover a number of areas. And the answers are deliberately wordy. In a programming interview, you’ll need to demonstrate your understanding of the language, and if you can explain it briefly, by all means do so. I tried to provide as much information as possible in the answers, so even if you have never known these areas before, you can learn something from the answers. I hope this article has helped you find satisfying work.