This article is from Python Data Science by wLsq

If you’ve already made it through a phone interview with a recruiter, it’s time to show off your coding prowess. Whether it’s exercises, assignments, or in-person whiteboard interviews, this is your time to prove your coding skills.

We know that interviewers will often ask you to solve some problems, but as a programmer, besides having ideas to solve problems, quality and simplicity of code are also important. Because a person’s code is a direct indicator of your fundamentals. In Python, this means that you need to have a deep understanding of Python’s built-in features and libraries.

Here are some of the most powerful features you can use to make an interviewer think you’re advanced, which can be a big plus for you. For these features, we started with Python’s built-in functions, followed by Python’s natural support for data structures, and finally, Python’s powerful standard library.

Choose the right built-in features

Python has a large standard library, but only a small library of built-in functions that are always available and do not need to be imported. Each of these is worth a closer look, but before we do, I’ll give you a few hints, especially in the case of some of these functions, about what might be a better alternative.

1. Use enumerate() instead of range() to iterate

Probably more than any other situation in an interview: You have a list of elements, and you need to traverse the list, accessing both the index and the value.

A classic coding interview problem called FizzBuzz can be solved by iterating over indexes and values. In FizzBuzz, you’ll get a list of integers and the task is to do the following:

  • Replace all integers divisible by 3 with “fizz”
  • Replace all integers divisible by 5 with “buzz”
  • Replace all integers divisible by 3 and 5 with “fizzbuzz”

Typically, developers will use range() to solve this problem:

>>> numbers = [45.22.14.65.97.72] > > >for i in range(len(numbers)):
...     if numbers[i] % 3= =0 and numbers[i] % 5= =0:... numbers[i] ='fizzbuzz'. elif numbers[i] %3= =0:... numbers[i] ='fizz'. elif numbers[i] %5= =0:... numbers[i] ='buzz'. >>> numbers ['fizzbuzz'.22.14.'buzz'.97.'fizz']
Copy the code

Range allows you to access numeric elements by index and is a useful tool for some special situations. But in this case, we want to get both the index and the value of each element, and a more elegant solution would use Enumerate () :

>>> numbers = [45.22.14.65.97.72] > > >for i, num in enumerate(numbers):
...     if num % 3= =0 and num % 5= =0:... numbers[i] ='fizzbuzz'. elif num %3= =0:... numbers[i] ='fizz'. elif num %5= =0:... numbers[i] ='buzz'. >>> numbers ['fizzbuzz'.22.14.'buzz'.97.'fizz']
Copy the code

Enumerate () returns a counter and element value for each element. The counter defaults to 0 and is also the index of the element. Don’t want to start your count at zero? Simply use the optional start parameter to set the offset:

>>> numbers = [45.22.14.65.97.72] > > >for i, num in enumerate(numbers, start=52):
...     print(i, num)
...
52 45
53 22
54 14
55 65
56 97
57 72
Copy the code

**** By using the start parameter, we access all the same elements, starting with the first index, but now we count from the specified integer value.

2. Construct lists recursively instead of maps () and filters ()

“I think removing filter() and map() is very controversial.”

-Guido van Rossum, creator of Python

Casual users may mistakenly think it is uncontroversial, but Guido has good reason to want to remove map() and filter() from Python. One reason is Python’s support for recursively constructed lists, which are generally easier to read and support the same functionality as map() and filter(). \

Let’s first look at how we construct the call to map() and the equivalent recursive construct list:

>>> numbers = [4.2.1.6.9.7]
>>> def square(x):
...     return x*x
...
>>> list(map(square, numbers))
[16.4.1.36.81.49]

>>> [square(x) for x in numbers]
[16.4.1.36.81.49]
Copy the code

Both methods using map() and list derivation return the same value, but list derivation is easier to read and understand. Here we can do the same for filter() and its equivalent list derivation:

>>> def is_odd(x):
...    return bool(x % 2)... >>>list(filter(is_odd, numbers))
[1.9.7]

>>> [x for x in numbers if is_odd(x)]
[1.9.7]
Copy the code

As we saw in map, filter and list derivation return the same value, but list derivation is easier to understand.

Developers from other languages may disagree that constructing lists is easier to read than maps and filters, but in my experience, beginners write list derivations more intuitively. But either way, using list inference in coding interviews rarely goes wrong, because it gives you an idea of what is most common in Python.

3. Use breakpoint() instead of print()

You might debug a small problem by adding print to your code and seeing what it prints out. This method worked well at first, but soon became cumbersome. Also, in coding interview Settings, you almost never want to call print() throughout your code.

Instead, you should use a debugger. It’s almost always faster than using print() for non-trivial errors, and since debugging is an important part of writing software, it shows that you know how to use tools that can be developed quickly on the job.

If you’re using Python 3.7, you don’t need to import anything, just call breakpoint() where you want to put the debugger in your code:

# Some complicated code with bugs

breakpoint()
Copy the code

**** Calling breakpoint() takes you to the PDB, which is the default Python debugger. In Python 3.6 and earlier, you can do the same by explicitly importing the PDB:

import pdb; pdb.set_trace()
Copy the code

Like breakpoint(), pdb.set_trace() takes you to the PDB debugger. It’s not that neat, and it’s a little bit more to remember. You may want to try other debuggers, but the PDB is part of the standard library, so it’s always available. No matter which debugger you prefer, it’s worth trying to use them to fit into your workflow before setting up your coding interview.

4. Use f-strings to format Strings

Python has many different ways to handle string formatting, and sometimes it’s not clear which one to use. In coding interviews, if Python 3.6+ is used, the recommended formatting method is Python F-strings.

F-strings supports the use of a string formatting mini-language, as well as powerful string interpolation. These features allow you to add variables and even valid Python expressions and evaluate them at run time before adding them to strings:

>>> def get_name_and_decades(name, age):
...     return f"My name is {name} and I'm {age / 10:.5f} decades old.". >>> get_name_and_decades("Maria".31)
My name is Maria and I'm 3.10000 decades old.
Copy the code

F-string allows you to put Maria into a string and add the age of the desired format in a neat operation. One risk to be aware of is that there may be a security risk if you output user-generated values, in which case template strings may be a safer choice.

5. Use sorted() to sort complex lists

A large number of coded interview questions require some sort of ranking, and there are several effective ways to do it. Unless the interviewer wants you to implement your own sorting algorithm, it’s usually best to use sorted(). You’ve probably seen the simplest use of sorting, such as sorting a list of numbers or strings in ascending or descending order:

>>> sorted([6.5.3.7.2.4.1[])1.2.3.4.5.6.7]

>>> sorted(['cat'.'dog'.'cheetah'.'rhino'.'bear'], reverse=True)
['rhino'.'dog'.'cheetah'.'cat'.'bear]
Copy the code

By default, sorted() already sorts the input in ascending order, while the reverse keyword arguments are sorted in descending order.

It’s worth knowing the optional key, which allows you to specify the function that will be called on each element before sorting. Add functions allow you to customize collation rules, which are especially useful if you want to sort more complex data types: \

>>> animals = [
...     {'type': 'penguin'.'name': 'Stephanie'.'age': 8},... {'type': 'elephant'.'name': 'Devon'.'age': 3},... {'type': 'puma'.'name': 'Moe'.'age': 5},
... ]
>>> sorted(animals, key=lambda animal: animal['age'[]) {'type': 'elephant'.'name': 'Devon'.'age': 3},
    {'type': 'puma'.'name': 'Moe'.'age': 5},
    {'type': 'penguin'.'name': 'Stephanie, 'age': 8},]Copy the code

You can easily sort the dictionary list by a single value for each dictionary by passing in a lambda function that returns the age of each element. In this case, the dictionary is now sorted in ascending order by age.

Use data structures effectively

Algorithms get a lot of attention in interviews, but data structures may be more important. In a coding interview environment, choosing the right data structure can have a significant impact on performance. In addition to theoretical data structures, Python has built powerful and convenient features into its standard data structure implementation. These data structures are very useful in an interview because by default they give you a lot of functionality that allows you to focus your time on other parts of the question.

1. Use set to store unique values

We usually need to remove duplicate elements from an existing dataset. New developers sometimes do this when lists should use collections, which enforces uniqueness of all elements.

Pretend you have a function called get_random_word(). It will always return a random selection from a small list of words:

>>> import random
>>> all_words = "all the words in the world".split()
>>> def get_random_word():
...    return random.choice(all_words)
Copy the code

You should call get_random_word() repeatedly to get 1000 random words and then return a data structure containing each unique word. Here are two common suboptimal approaches and a good one.

Bad method

Get_unique_words () stores values in a list and then converts the list to a collection:

>>> def get_unique_words():
...     words = []
...     for _ in range(1000):
...         words.append(get_random_word())
...     return set(words)
>>> get_unique_words()
{'world'.'all'.'the'.'words'}
Copy the code

**** this approach is not terrible, but it needlessly creates a list and then turns it into a collection. Interviewers almost always notice (and ask about) this type of design choice.

Even worse

To avoid converting from a list to a collection, you can now store values in a list without using any other data structures. The uniqueness is then tested by comparing the new value to all the current elements in the list:

>>> def get_unique_words():
...     words = []
...     for _ in range(1000):
...         word = get_random_word()
...         if word not in words:
...             words.append(word)
...     return words
>>> get_unique_words()
['world'.'all'.'the'.'words']
Copy the code

This is worse than the first method because you have to compare every new word to every word already in the list. This means that the number of lookups increases quadratic as the number of words increases. In other words, time complexity increases on the order of O(N^2).

Excellent method

Now, you skip the list altogether and start with a set from scratch:

>>> def get_unique_words():
...     words = set()
...     for _ in range(1000):
...         words.add(get_random_word())
...     return words
>>> get_unique_words()
{'world'.'all'.'the'.'words'}
Copy the code

Other than using collections from scratch, this might not be that different from other approaches. If you consider what happens in.add(), it even sounds like the second approach: get the word, check to see if it’s already in the collection, and if not, add it to the data structure.

So why use a different set than the second method?

They are different because the collection stores elements in a way that allows a near-constant time check to see if the value is in the collection, unlike a list that requires a linear time lookup. The difference in finding times means that the time complexity added to the collection grows at an O(N) rate, which in most cases is much better than the O(N^2) of the second method.

2. Use generators to save memory

As mentioned earlier, list derivation is a handy tool, but can sometimes lead to unnecessary memory usage. Imagine that you were asked to find the sum of the first 1,000 perfect squares, starting at 1. You know the list derivation, so you quickly write an effective solution:

>>> sum([i * i for i in range(1.1001)])
333833500
Copy the code

The solution lists every perfect square between 1 and 1,000,000 and sums the values. Your code will return the correct answer, but then your interviewer will start increasing the number of perfect squares you need to sum up.

At first, your feature keeps popping up the right answers, but soon begins to slow down, until eventually the process seems to go on forever. This is not the one thing you want to happen in an interview.

What’s going on here?

It’s listing every perfect square you asked for and adding them all up. A list of 1,000 perfect squares might not be huge in computer terms, but 100 million or a billion is a lot of information, and can easily take up a computer’s available memory resources. That’s what happened here.

Thankfully, there is a quick way to solve the memory problem. You simply replace square brackets with parentheses:

>>> sum((i * i for i in range(1.1001)))
333833500
Copy the code

Swapping out parentheses changes the list derivation to a generator expression. Generator expressions are great when you know you want to retrieve data from a sequence, but don’t need to access all of it at the same time.

Generator expressions return generator objects instead of creating lists. The object knows its position in the current state (for example, I = 49) and only computes the next value when asked.

Thus, when sum iterates over the generator object by repeatedly calling.__ next __(), the generator checks for I

Is equal to what, evaluates I * I, increments I internally, and returns the correct value to sum. This design allows generators to be used with large data sequences because only one element exists in memory at a time.

3. Define defaults in the dictionary using.get() and.setdefault()

One of the most common programming tasks involves adding, modifying, or retrieving items that may or may not be in the dictionary. Python dictionaries have elegant features that make these tasks straightforward, but developers often check values when they don’t need to.

Imagine that you have a dictionary called Cowboy and you want to get that cowboy’s name. One way is to use conditional explicit check keys:

>>> cowboy = {'age': 32.'horse': 'mustang'.'hat_size': 'large'} > > >if 'name' in cowboy:
...     name = cowboy['name']...else:... name ='The Man with No Name'. >>> name'The Man with No Name'
Copy the code

This method first checks for the presence of the name key in the dictionary and returns the corresponding value if it does. Otherwise, it returns the default value.

While clearly checking the key does work, it can easily be replaced with a line if.get() is used:

>>> name = cowboy.get('name'.'The Man with No Name')
Copy the code

Get () performs the same operations as the first method, but now they are handled automatically. If key exists, the appropriate value is returned. Otherwise, the default value is returned.

But what if you want to update the dictionary with the default value while still accessing the key of name? .get() doesn’t really help you here, so you just need to explicitly check the value again:

>>> if 'name' not in cowboy:
...     cowboy['name'] = 'The Man with No Name'. >>> name = cowboy['name']
Copy the code

Checking values and setting defaults is a valid method and easy to read, but Python again uses.setdefault() to provide a more elegant method:

>>> name = cowboy.setdefault('name'.'The Man with No Name')
Copy the code

.setdefault() does exactly the same as the code snippet above. It checks for the presence of a name in Cowboy and returns that value if so. Otherwise, it sets cowboy [‘name’] to The Man with No name and returns The new value.

Make use of Python’s standard library

By default, Python provides a lot of functionality, which is just an import statement. It’s powerful in its own right, but knowing how to leverage the standard library can enhance your coding interview skills.

It can be difficult to pick the most useful parts from all the available modules, so this section will focus on only a small portion of the actual functionality. Hopefully these will be useful to you in your coding interviews, and you want to learn more about the advanced features of these and other modules.

1. Use collections.defaultdict() to handle missing dictionary keys

.get() and.setdefault() work fine when you set defaults for individual keys, but you usually need to set defaults for all possible unset keys, especially when programming in an interview environment.

Pretend you have a group of students and you need to keep track of their grades on homework. The input values are a tuple list with format (student_name, grade), but you want to easily find all grades for a single student without iterating through the list.

One way to store grade data is to use a dictionary that maps a student’s name to a grade list:

>>> student_grades = {}
>>> grades = [
...     ('elliot'.91),... ('neelam'.98),... ('bianca'.81),... ('elliot'.88),
... ]
>>> for name, grade in grades:
...     if name not in student_grades:
...         student_grades[name] = []
...     student_grades[name].append(grade)
...
>>> student_grades
{'elliot': [91.88].'neelam': [98].'bianca': [81]}
Copy the code

In this approach, you iterate over students and check to see if their names are already attributes in the dictionary. If not, they are added to the dictionary with an empty list as the default. The actual score is then appended to the student’s score list.

But a more succinct way is to use DefaultDict, which extends the standard dict functionality and allows you to set a default value that will act on if the key isn’t present:

>>> from collections import defaultdict
>>> student_grades = defaultdict(list) > > >for name, grade in grades:
...     student_grades[name].append(grade)
Copy the code

In this case, you’ll create a Defaultdict that uses the list constructor with no arguments as the default method. A list with no arguments returns an empty list, so defaultdict calls list() if the name doesn’t exist, and then adds the student score. If you want to be more fancy, you can also use lambda functions as values to return arbitrary constants.

Using DefaultDict makes the code cleaner because you don’t have to worry about the default values for keys. Instead, you can process them once in DefaultDict, and the keys will eventually exist.

2. Calculate Hashable objects using collections.counter

Suppose you have a long list of words without punctuation or capital letters, and you want to count the number of occurrences of each word.

You can augment the count using dictionaries or Defaultdict, but collections.counter provides a clearer and more convenient way. Counter is a dict subclass that uses 0 as the default for any missing element and makes it easier to count occurrences of objects:

>>> from collections import Counter
>>> words = "if there was there was but if \
... there was not there was not".split()
>>> counts = Counter(words)
>>> counts
Counter({'if': 2.'there': 4.'was': 4.'not': 2.'but': 1})
Copy the code

When you pass the list of words to Counter, it stores each word and the number of times that word appears in the list.

If you’re wondering what the two most common words are? Just use.most_common () :

>>> counts.most_common(2)
[('there'.4), ('was'.4)]
Copy the code

.most-common () is a convenient method that simply returns n of the most frequent inputs by counting.

3. Use string constants to access common string groups

Now there’s a trivia to judge! Is’ A ‘>’ A ‘true or false?

This is false because the ASCII code for A is 65, but A is 97, 65 is not greater than 97. Why does the answer matter? Because if you want to check if A character is part of the English alphabet, A popular way is to see if it is between A and Z (65 and 122 on the ASCII chart).

Checking ASCII codes is doable, but it can be awkward and confusing in an interview, especially if you can’t remember whether lowercase or uppercase ASCII characters come first. At this point, it is much easier to use constants defined in the string module.

You can use is_upper(), which returns whether all characters in the string are uppercase:

>>> import string
>>> def is_upper(word):
...     for letter in word:
...         if letter not in string.ascii_uppercase:
...             return False.return True. >>> is_upper('Thanks Geir')
False
>>> is_upper('LOL')
True
Copy the code

Is_upper () iterates over the letters in Word and checks if the letter is part of the string.ascii_ uppercase letter. If you print out the string. Ascii_ caps, you will find that it is a string, the value is set to text “ABCDEFGHIJKLMNOPQRSTUVWXYZ”.

All string constants are just strings of frequently referenced string values. These include the following:

  • string.ascii_letters
  • string.ascii_uppercase
  • string.ascii_lowercase
  • string.digits
  • string.hexdigits
  • string.octdigits
  • string.punctuation
  • string.printable
  • string.whitespace

These are easier to use and, more importantly, easier to read.

4. Use Itertools to generate permutations and combinations

Interviewers like to give real life scenarios to make the interview seem less intimidating, so here’s an artificial example: You go to an amusement park and decide to track down every couple of friends who might be on a roller coaster.

Unless generating these pairs is the primary purpose of the interview question, it’s likely that generating all possible pairs is just a tedious step toward a working algorithm. You can evaluate them yourself with a nested for loop, or you can use the powerful IterTools library.

Itertools has several tools to generate repeatable sequences of input data, but for now we’ll focus on two common functions: itertools. Permutations () and itertools.combinations().

Itertools.permutations () builds a list of all permutations, which means that it is a list of every possible grouping of the input values, whose length matches the count argument. The r keyword argument allows us to specify how many values are in each group:

>>> import itertools
>>> friends = ['Monique'.'Ashish'.'Devon'.'Bernie'] > > >list(itertools.permutations(friends, r=2[())'Monique'.'Ashish'), ('Monique'.'Devon'), ('Monique'.'Bernie'),
('Ashish'.'Monique'), ('Ashish'.'Devon'), ('Ashish'.'Bernie'),
('Devon'.'Monique'), ('Devon'.'Ashish'), ('Devon'.'Bernie'),
('Bernie'.'Monique'), ('Bernie'.'Ashish'), ('Bernie'.'Devon')]
Copy the code

For permutations, the order of the elements is important, so (” Sam “, “Devon”) represents a different pairing from (” Devon “, “Sam”), which means they will all be included in the list.

Itertools.binations () generates combinations. These are also possible groupings of input values, but for now the order of the values doesn’t matter. Because (‘ Sam ‘, ‘Devon’) and (‘ Devon ‘, ‘Sam’) represent the same pair, only one of them will be included in the output list:

>>> list(itertools.combinations(friends, r=2[())'Monique'.'Ashish'), ('Monique'.'Devon'), ('Monique'.'Bernie'),
('Ashish'.'Devon'), ('Ashish'.'Bernie'), ('Devon'.'Bernie')]
Copy the code

Because the order of values is associated with composition, there are fewer combinations than permutations of the same input list. Again, because we set r to 2, there are two names in each group.

.combinations and.permutations are just a small example of the powerful libraries available, but even these functions can be useful when you’re trying to solve algorithmproblems quickly.

In your next interview, you can safely use some of the less common but more powerful standard features. There’s a lot to learn about the language as a whole, but this article should give you a starting point to learn more about the language and use Python more effectively in your interview.


Note: the menu of the official account includes an AI cheat sheet, which is very suitable for learning on the commute.

Machine Learning Online Manual Deep Learning online Manual AI Basics download (PDF updated to25Note: to join this site's wechat group or QQ group, please reply to "add group" to get a discount station knowledge planet coupon, please reply to "knowledge planet"Copy the code

Like articles, click Looking at the