This article is exclusively compiled by Earlgrey@programmer. please note the author and source.
Sheena@codementor 原文 : programming school
The introduction
Looking for a Python development job? Then you’ll probably have to prove that you know how to use Python. The following questions address a number of Python-related skills and focus on the language itself, not a specific package or module. Each question can be expanded into a tutorial, if possible. Some of the questions may even cover more than one area.
I’ve never had an interview question as difficult as these, so if you can answer them easily, get a job!
Question 1
What exactly is Python? You can compare your answers to other techniques (and this is encouraged).
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 write something like this directly
x=111
andx="I'm a string"
Such code, the program will not report errors. - Python is well suited for OOP because it allows classes to be defined through composition and inheritance. There is no access specifier in Python (similar to C++)
public
andprivate
), based on the idea that “we’re all adults.” - 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
This is a great example, and it’s really fast, because a lot of the 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 code
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 sChildPath
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 the
os
The module interacts with the operating system, and the way of interaction can be cross-platform. You can write the code assChildPath = 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]
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]]
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.
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 (x, f l = []) : for I in range (x) : L.A. ppend (I * I) print l f (2) f (3, [3, 2, 1]) f (3)
The answer:
[0, 1] [3, 2, 1, 0, 1, 4]
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]
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)
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?
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}
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.
@my_decorator def my_func(stuff): do_things Is equivalent to def my_func(stuff): do_things my_func = my_decorator(my_func)
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 # > o.normal_method() # normal_method((<__main__.myclass instance="" at="" 0x7fdd2537ea28="">,),{}) O.n ormal_method (1, 2, x = 3, y = 4) # normal_method ((< __main__. Myclass instance = "" at x7fdd2537ea28 =" "> =" "0, 1, 2), {' y ': 4, 'x': The first argument to a class method is always the class o.class_method # > o.class_method() # class_method((,),{}) o.class_method(1,2,x=3,y=4) # Class_method ((, 1, 2),{'y': 4, 'x': 3}) {'y': 4, 'x': 3}) Grown tatic_method # grown tatic_method () # static_method ((), {}) grown tatic_method (1, 2, x = 3, y = 4) # static_method ((1, 2), {' y ': 4, 'x': 3}) # @property is a way to implement getters and setters. It is an error to call them directly. The "read-only" property can be implemented by defining only getter methods, not setter methods. Myclass instance="" at="" 0x7FB2B70877E8 ="">,(),{}) # 'properties are O.home_property () # calling some_property getter(<__main__. Myclass instance="" at="") 0x7fb2b70877e8="">,(),{}) # Traceback (most recent call last): # File "", line 1, in # 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 "", line 1, in # 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 "", line 1, in # AttributeError: can't set attribute o.some_other_property # calling some_other_property getter(<__main__.myclass object="" at="" 0x7fb2b7077890="">,(),{})
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()
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 Implemented
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 "".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) #
ORoot. Print_all_1 () prints the following:
ORoot. Print_all_1 () prints the following:
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.
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
Reference cycle
(Reference Cycle). The garbage collector periodically looks for the loop and collects it. For example, suppose you have two objectso1
ando2
And in accordance witho1.x == o2
ando2.x == o1
These two conditions. ifo1
ando2
No other code references them, 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)]
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)')
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:1 (F1) 1 0.001 0.001 0.045 0.045:1 () 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects'} 1 0.035 0.035 0.035 0.035 >>> cprofile. run('f2(lIn)') 4 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:1 (f2) 1 0.001 0.001 0.024 0.024:1 () 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.016 0.016 0.016 0.016 >>> cprofile. run('f3(lIn)') 4 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:1 (f3) 1 0.001 0.001 0.055 0.055:1 () 1 0.000 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.038 0.038 0.038 0.038 0.038 {sorted}
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.
Come on!