FullStack Labs

Please Upgrade Your Browser.

Unfortunately, Internet Explorer is an outdated browser and we do not currently support it. To have the best browsing experience, please upgrade to Microsoft Edge, Google Chrome or Safari.
Welcome to FullStack Labs. We use cookies to enable better features on our website. Cookies help us tailor content to your interests and locations and provide many other benefits of the site. For more information, please see our Cookies Policy and Privacy Policy.

Hire our Elite Python Developers

FullStack Labs is a leading team of Python developers, engineers, programmers, coders, and architects. Uber, Siemens, and hundreds of other companies have chosen us for their mission critical software development projects.

Hire Our Elite python Developers Now
What's included

We’ll review your code base and provide a report that includes quality scores for the following:

  • Test suite and test coverage
  • Code quality, including a CodeClimate report
  • How soon can FullStack start on my project?
  • Dependencies
  • Error and performance monitoring
  • Documentation
Python Logo
Trusted By:
UberSiemensEricssonNFIBEkso BionicsCalifornia

Some of our expert Python Developers.

By combining USA and Canada technical leads and project managers with Senior Developers in Latin America, we offer clients the quality, security, and convenience of onshore development paired with the cost savings of nearshore development.
Daniel Roma
Senior Software Engineer
Location icon
Latin America
Star icon
 Years of Experience

I became a developer not only to learn how computers work but also how to use them to build new things. This led me first to understand and work on the hardware, and after a few jobs in IT positions, I switched to software development to get the full picture.

Mariano Dilascio
Senior Software Engineer
Location icon
Latin America
Star icon
 Years of Experience

Senior Software Engineer, Mariano Dilascio, Latin America

Pablo Cordero
Software Architect
Location icon
Latin America
Star icon
 Years of Experience

Senior Software Engineer, Ruby on Rails Developer

Ben Carle
Location icon
Star icon
 Years of Experience

As the CIO of FullStack Labs I'm directly responsible for managing and contributing to our most critical client projects. I'm a senior software engineer with 15 years of experience building complex custom software and leading development teams.

Christian Suárez
Senior Software Engineer
Location icon
Latin America
Star icon
 Years of Experience

As a Senior Software Engineer, I have a passion for computers and technology. I believe there's a lot of value in the data that we generate on our apps and computers that could help to discover insights useful for solving a large number of society's problems.

David Watts
Software Architect
Location icon
Star icon
 Years of Experience

As a Senior Software Engineer at Fullstack Labs, I am an expert in building high-performance, scalable applications and deploying enterprise-level systems across diverse industries.

Python Frameworks We Love

Flask Logo

A barebones framework for simple projects, we choose Flask for focused, straight-forward apps.

Django Logo

An all inclusive, opinionated framework, we choose django for robust applications that require specific feature sets.

Custom icon

No framework, no problem. Our team of expert Python developers is well versed in scripting, provisioning, and working with custom code.

Client Testimonials

The experience I've had with other firms is in direct contrast to what FullStack did. I'm comparing them with two other firms I worked with in the past. They care a lot about what their clients are doing. They bring their ideas and concepts to the table. I felt like they were on my team. They added a lot of value in terms of ideas and continue to do so. No matter who's working on the project you feel like they care about the work that's being produced.

- Rob Burns -
President, Bunk 1

FullStack Labs' managers have been engaged throughout. The whole project management staffing, matching team to task has been fantastic. All their staff is very committed. I'm very aware of how much bureaucracy there is in the tech world, and we've gotten nothing but solutions. I am totally happy with their approach to everything, their integrity, honesty, the whole thing. It's great.

- Executive Director -
Valley CAN

We were very happy with the way the whole process was facilitated. We started talking to FullStack Labs three months before we signed any contract with them. We met with them in person several times. We wanted to find out what their philosophy is. Both Brian and David are fathers with young children, so they understand the importance of the work we are doing. That shows through in their work. It was obvious they cared deeply.

- Anna Sadovnikova -
CEO, MomKit

How to Hire Python Developers Through FullStack Labs

  • 1. Talk to us!

    We will work with you to understand your technical needs, team dynamics, and goals.

  • 2. Meet our available talent

    We’ll send you FullStack Python developers that match your technical requirements, with links to their FullStack profile page which outlines their work experience and technical abilities, as well as their FullStack technical summary page, which includes a 60 minute video of the developer completing FullStack’s coding challenge, and a plethora of other technical information from their interview with us.

  • 3. 14 Day Risk-free trial

    Start the engagement. Work with your new Python developers for a trial period, ensuring they're the right fit.

    Check icon

Why Hire Python Developers from FullStack Labs?


Expert Specialists

At FullStack Labs, we’re all about excellence in software development. That’s why we hire the best Python Developers in the US and Latin America and pair you up with seasoned experts that fill your talent gaps and deliver critical value to your project. 


Scalable Engagements

From a single Python engineer to a fully-featured software development team, we provide you with solutions that allow you to scale up and down as needed. All of our contracts are month-to-month and can be canceled with 30-days notice.


Daily Reporting

Each day you'll receive a detailed report that shows all hours billed the previous day with a description of what was completed, as well as month-to-date and year-to-date totals. This applies to all projects, regardless of size or complexity.

flexible engagements

Transparent Billing

Our detailed management and billing process is focused on transparency, and we use it to measure the efficiency of our developers. This way, you only pay for the hours we work—no weekly or monthly block billing.


Seamless Onboarding

Most of our engineers onboard to client projects in less than 2 weeks and immediately ramp up development and project contributions. We handle all of the onboarding overhead so that you and your team can focus on building great software.


Global Talent

Our talent is distributed across more than 17 countries in Central and South America. We’re able to reach a large pool of talent, always making sure to assign the region’s top engineers to your project.

Engagement Models for Python Projects

New Python Apps

We design and build greenfield apps of all shapes and sizes using Python combined with a React.js, Ember.js or Angular.js javascript framework.

Existing Python Apps

Have a legacy Python app? We’re here to help. From debugging and maintenance to feature development and DevOps, we'll tailor a development plan to meet your needs.

Python Team Augmentation

Need to add a Python developer to your existing team? We'll seamlessly integrate as many Python developers as needed, to help you go faster and level up your team's skills.

FullStack Labs is proudly remote.

We have an ever growing team of incredible people currently located in these countries.
Meet our Leadership Team
America continent map with Fullstack Labs members' locations highlighted in green spot.
United States flag
United States
Mexico flag
Guatemala flag
Dominican Republic flag
D. Republic
Honduras flag
Costa Rica flag
Costa Rica
El Salvador flag
El Salvador
Panama flag
Nicaragua flag
Venezuela flag
Colombia flag
Ecuador flag
Peru flag
Brazil flag
Paraguay flag
Argentina flag
Uruguay flag
Meet our Leadership Team

Our Clients Love Us, And You Will Too.

  • Yelp Logo
  • Thumbtack Logo
  • Clutch Logo
  • Good Firms Logo
  • Glassdoor Logo
FullStack Labs Icon

Let's Talk!

We’d love to learn more about your project.
Engagements start at $75,000.

company name
Type of project
How did you hear about us?
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.


At FullStack Labs,  we hire the best Python developers, engineers, programmers, coders, and architects in the US and Latin America. Uber, Siemens, and hundreds of other companies have chosen us for their mission-critical software development projects. 

Since many of our clients like to interview our developers before onboarding them to their projects, we’ve put together a few Essential Python Interview Questions you can use to screen the candidates in your shortlist.

Q.1a - What will be the output of the code below? Explain your answer.
def extendList(val, list=[]):
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3


The output of the above code will be:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

Many will mistakenly expect list1 to be equal to [10] and list3 to be equal to ['a'], thinking that the list argument will be set to its default value of [] each time extendList is called. However, this is not the case. Instead, the value of list1 changes from [10] to [10, ‘a’] as soon as we call extendList(‘a’) and define list3. This is how that looks in code:

list1 = extendList(10)
print "list1 = %s" % list1
>>> list1 = [10]

list3 = extendList('a')
print "list1 = %s" % list1
>>> list1 = [10, 'a']

But why does this happen? Well, that’s because the default list in the extendList function is created only once—which happens when the function is defined. After that, every resulting list from a function call will be used subsequently whenever extendList is invoked without a specified list argument. In other words, the expressions in default arguments are calculated when the function is defined, and not when it’s called.

Therefore, list1 and list3 are operating on the same default list, whereas list2 is operating on the list defined by the second argument, bypassing its own empty list as the value for the list parameter. The same would apply in case a list4 didn’t specify a list argument:

list4 = extendList('b')
print "list1 = %s" % list1
>>> list1 = [10, 'a', 'b']

Q.1b - How would you modify the definition of extendList to produce the presumably desired behavior?


The definition of the extendList function can be easily modified to achieve the commonly mistaken expectation of this script. The trick is to always create a new list when no list argument is specified:

def extendList(val, list=None):
  if list is None:
    list = []
  return list

With this revised implementation, the output would be:


list1 = [10]
list2 = [123]
list3 = ['a']

Q.2a - What will be the output of the code below? Explain your answer.
def multipliers():
  return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]


The output of the above code is [6, 6, 6, 6] and not [0, 2, 4, 6].

This is because Python’s closures are late binding, which means that the values of variables used in closures are looked up at the time the inner function is called. So, as a result, when any of the functions returned by multipliers() are called, the value of i is looked up in the surrounding scope at that time. By then, regardless of which of the returned functions is called, the for loop has completed and i is left with its final value of 3. Therefore, every returned function multiplies the value it is passed by 3, so since a value of 2 is passed in the above code, they all return a value of 6 (i.e., 3 x 2).

Incidentally, as pointed out in The Hitchhiker’s Guide to Python, there is a somewhat widespread misconception that this has something to do with lambdas, which is not the case. Functions created with a lambda expression are in no way special and exhibit the same behavior as any ordinary function.

Q.2b - How would you modify the definition of multipliers to produce the presumably desired behavior?


Below are a few examples of ways to circumvent this issue.

One solution would be to use a Python generator as follows:

def multipliers():
  for i in range(4): yield lambda x : i * x 

Another solution is to create a closure that binds immediately to its arguments by using a default argument. For example:

Or alternatively, you can use the functools.partial function:

from functools import partial
from operator import mul

def multipliers():
  return [partial(mul, i) for i in range(4)]

Finally, the easiest fix may be to simply replace the return value’s [] with ():

def multipliers():
  return (lambda x : i * x for i in range(4))

Q.3 - What will be the output of the code below? Explain your answer
class Parent(object):
    x = 1

class Child1(Parent):

class Child2(Parent):

print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x


The output of the above code will be:

1 1 1
1 2 1
3 2 3

What confuses or surprises many about this is that the last line of output is 3 2 3 rather than 3 2 1. Why does changing the value of Parent.x also change the value of Child2.x, but at the same time not change the value of Child1.x?

The key to the answer is that, in Python, class variables are internally handled as dictionaries. If a variable name is not found in the dictionary of the current class, the class hierarchy (i.e., its parent classes) are searched until the referenced variable name is found (if the referenced variable name is not found in the class itself or anywhere in its hierarchy, an AttributeError occurs).

Therefore, setting x = 1 in the Parent class makes the class variable x (with a value of 1) referenceable in that class and any of its children. That’s why the first print statement outputs 1 1 1.

Subsequently, if any of its child classes overrides that value (for example, when we execute the statement Child1.x = 2), then the value is changed in that child only. That’s why the second print statement outputs 1 2 1.

Finally, if the value is then changed in the Parent (for example, when we execute the statement Parent.x = 3), that change is reflected also by any children that have not yet overridden the value (which in this case would be Child2). That’s why the third print statement outputs 3 2 3.

Q.4a - What will be the output of the code below? Explain your answer.
def div1(x,y):
    print "%s/%s = %s" % (x, y, x/y)
def div2(x,y):
    print "%s//%s = %s" % (x, y, x//y)



In Python 2, the output of the above code will be:

5/2 = 2
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0

By default, Python 2 automatically performs integer arithmetic if both operands are integers. As a result, 5/2 yields 2, while 5./2 yields 2.5. Note that you can override this behavior in Python 2 by adding the following import:

from __future__ import division

Also note that the “double-slash” (//) operator will always perform integer division, regardless of the operand types. That’s why 5.0//2.0 yields 2.0 even in Python 2.

Q.4b - Also, how would the answer differ in Python 3 (assuming, of course, that the above print statements were converted to Python 3 syntax)?


In Python 3, division behaves differently than in Python 2; i.e., it does not perform integer arithmetic if both operands are integers. Therefore, in Python 3, the output will be as follows:

Q.5 - What will be the output of the code below?
list = ['a', 'b', 'c', 'd', 'e']
print list[10:]


The above code will output [], and will not result in an IndexError.

As one would expect, attempting to access a member of a list using an index that exceeds the number of members (e.g., attempting to access list[10] in the list above) results in an IndexError. However, attempting to access a slice of a list at a starting index that exceeds the number of members in the list will not result in an IndexError and will simply return an empty list.

What makes this particularly tricky is that it can lead to bugs that are really hard to track down since no error is raised at runtime.

Q.6 - Consider the following code snippet, what will be the output of lines 2, 4, 6, and 8? Explain your answer.
1. list = [ [ ] ] * 5
2. list  # output?
3. list[0].append(10)
4. list  # output?
5. list[1].append(20)
6. list  # output?
7. list.append(30)
8. list  # output?


The output will be as follows:

[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

Here’s why:

The first line of output is presumably intuitive and easy to understand; i.e., list = [ [ ] ] * 5 simply creates a list of 5 lists.

However, the key thing to understand here is that the statement list = [ [ ] ] * 5 does not create a list containing 5 distinct lists; rather, it creates a list of 5 references to the same list. With this understanding, we can better understand the rest of the output.

list[0].append(10) appends 10 to the first list. But since all 5 lists refer to the same list, the output is: [[10], [10], [10], [10], [10]].

Similarly, list[1].append(20) appends 20 to the second list. But again, since all 5 lists refer to the same list, the output is now: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]].

In contrast, list.append(30) is appending an entirely new element to the “outer” list, which therefore yields the output: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30].

Q.7 - What is the solution to the following list comprehension problem?

Given a list of N numbers, use a single list comprehension to produce a new list that only contains values that comply with the following requirements: 

  1. The value is an even number. 
  2. The value’s index in the original list is an even number.  

For example, if list[2] contains a value that is even, that value should be included in the new list, since it is also at an even index (i.e., 2) in the original list. However, if list[3] contains an even number, that number should not be included in the new list since it is at an odd index (i.e., 3) in the original list


A simple solution to this problem would be as follows:

[x for x in list[::2] if x%2 == 0]

For example, given the following list

#        0   1   2   3    4    5    6    7    8
list = [ 1 , 3 , 5 , 8 , 10 , 13 , 18 , 36 , 78 ]

the list comprehension [x for x in list[::2] if x%2 == 0] will evaluate to:

[10, 18, 78]

The expression works by first taking the numbers that are at the even indices, and then filtering out all the odd numbers.

Q.8 - Will this code work? Why or why not?

Given the following subclass of dictionary:

class DefaultDict(dict):
  def __missing__(self, key):
    return []

will the code below work? Why or why not?

d = DefaultDict()
d['florp'] = 127


Actually, the code shown will work with the standard dictionary object in Python 2 or 3—that is normal behavior. Subclassing dict is unnecessary. However, the subclass still won’t work with the code shown because __missing__ returns a value but does not change the dict itself:

d = DefaultDict()
print d
print d['foo']
print d

So it will “work,” in the sense that it won’t produce any error, but doesn’t do what it seems to be intended to do.

Here is a __missing__-based method that will update the dictionary, as well as return a value:

class DefaultDict(dict):
    def __missing__(self, key):
        newval = []
        self[key] = newval
        return newval

Also, note that a defaultdict object has been available in the standard library since version 2.5 of Python.

Q.9 -How would you unit-test the following code?
async def logs(cont, name):
    conn = aiohttp.UnixConnector(path="/var/run/docker.sock")
    async with aiohttp.ClientSession(connector=conn) as session:
        async with session.get(f"http://xx/containers/{cont}/logs?follow=1&stdout=1") as resp:
            async for line in resp.content:
                print(name, line)


A good answer would suggest a specific async mock library and async test case approach, including an ephemeral event loop that’s guaranteed to terminate (i.e. with a max number of steps before timeout.)

A great answer would point out that synchronization problems are fundamentally the same in synchronous and asynchronous code, the difference being preemption granularity.

And an even better answer would take into account that the above code only has one flow (easy) compared to some other code where flows are mixed (e.g. merging two streams into one, sorting, etc). For example, consider the following upgrade to the given code:

keep_running = True

async def logs(cont, name):
    conn = aiohttp.UnixConnector(path="/var/run/docker.sock")
    async with aiohttp.ClientSession(connector=conn) as session:
        async with session.get(f"http://xx/containers/{cont}/logs?follow=1&stdout=1") as resp:
            async for line in resp.content:
                if not keep_running:
                print(name, line)

Here, any of the async statements could have a side-effect of changing the global keep_running.

Q.10 - How do you list the functions in a module?


Use the dir() method to list the functions in a module.

For example:

import some_module
print dir(some_module)

Q.11 - Can you write a function that solves the following problem?

Your function must print the least integer that is not present in a given list and cannot be represented by the summation of the sub-elements of the list. For example, in the list a = [1,2,5,7] the least integer not represented by the list or a slice of the list is 4, and if a = [1,2,2,5,7] then the least non-representable integer is 18.


import  itertools
sum_list = []
stuff = [1, 2, 5, 7]
for L in range(0, len(stuff)+1):
    for subset in itertools.combinations(stuff, L):

new_list = list(set(sum_list))
for each in range(0,new_list[-1]+2):
    if each not in new_list: