<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>tutorial | Cody VanZandt</title><link>https://codyvanzandt.com/category/tutorial/</link><atom:link href="https://codyvanzandt.com/category/tutorial/index.xml" rel="self" type="application/rss+xml"/><description>tutorial</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Sat, 15 Sep 2018 00:00:00 +0000</lastBuildDate><image><url>https://codyvanzandt.com/images/icon_hu596349aac3686149471565001a3f5f6a_378825_512x512_fill_lanczos_center_2.png</url><title>tutorial</title><link>https://codyvanzandt.com/category/tutorial/</link></image><item><title>Better Coding Through Shakespeare</title><link>https://codyvanzandt.com/blog/better-coding-through-shakespeare/</link><pubDate>Sat, 15 Sep 2018 00:00:00 +0000</pubDate><guid>https://codyvanzandt.com/blog/better-coding-through-shakespeare/</guid><description>&lt;p>Shakespeare, as Ben Jonson reminds us, is &amp;ldquo;not of an age but for all time.&amp;rdquo;
So, naturally, he has lots of good advice on computer programming.&lt;/p>
&lt;p>This 7 part series draws on Shakespeare&amp;rsquo;s plays and Robert C. Martin&amp;rsquo;s &lt;em>Clean Code&lt;/em> to illustrate some essential programming practices that rarely get covered in conventional &amp;ldquo;Learn to Code!&amp;rdquo; contexts. By the end of this post, you&amp;rsquo;ll have quality answers to some of programming&amp;rsquo;s toughest questions. What separates good variable names from bad ones? How big should functions be? When are comments necessary?&lt;/p>
&lt;p>This blog post answers all of these questions (and more!) with level-headed pragmatism and a sizeable dollop of Shakespearean humor. So, sit back, relax, and enjoy&amp;hellip;&lt;/p>
&lt;h1 id="better-coding-through-shakespeare">Better Coding Through Shakespeare&lt;/h1>
&lt;h2 id="act-i-naming-things-or-romeo-is-a-dreadful-programmer">Act I: Naming Things. Or, Romeo Is a Dreadful Programmer&lt;/h2>
&lt;p>In &lt;em>Romeo and Juliet&lt;/em>, Romeo famoulsy opines&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>What’s in a name? That which we call a rose&lt;/em>&lt;/p>
&lt;p>&lt;em>By any other name would smell as sweet.&lt;/em>&lt;a href="#romeo">[1]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Now that might be a fine philosophy for Montagues and Capulets, but it&amp;rsquo;s a &lt;em>dreadful&lt;/em> way to approach programming.&lt;/p>
&lt;p>&lt;strong>Names matter&lt;/strong>.&lt;/p>
&lt;p>Well-named variables, functions, and classes can significantly improve the readability and maintainability of a program.&lt;/p>
&lt;p>For example, check out this function:&lt;/p>
&lt;pre>&lt;code class="language-python">def get_result(l):
result = list()
for i, j in l:
if j &amp;gt; 40:
result.append(i)
return result
&lt;/code>&lt;/pre>
&lt;p>To quote Robert Martin,&lt;/p>
&lt;blockquote>
&lt;p>The problem isn’t the simplicity of the code but the &lt;em>implicity&lt;/em> of the code. &lt;a href="#clean1">[2]&lt;/a>
You might know &lt;em>what&lt;/em> this code does, but you have no idea &lt;em>why&lt;/em> it&amp;rsquo;s doing it. What&amp;rsquo;s the significance of the number 40? What&amp;rsquo;s &lt;code>i&lt;/code>? What&amp;rsquo;s &lt;code>j&lt;/code>?
What are the inputs? The outputs?&lt;/p>
&lt;/blockquote>
&lt;p>You just don&amp;rsquo;t know.&lt;/p>
&lt;p>But with a little thoughtful renaming, everything becomes clear:&lt;/p>
&lt;pre>&lt;code class="language-python">OVERTIME_CUTOFF = 40
def get_employees_with_overtime(employees_and_hours):
employees_with_overtime = list()
for employee_name, hours_worked in employees_and_hours:
if hours_worked &amp;gt; OVERTIME_CUTOFF:
employees_with_overtime.append(employee_name)
return employees_with_overtime
&lt;/code>&lt;/pre>
&lt;p>Ah, that&amp;rsquo;s better! Now you know &lt;em>exactly&lt;/em> what this function and all of its variables are for.&lt;/p>
&lt;h4 id="see-names-as-opportunities-to-add-value">See Names as Opportunities to Add Value&lt;/h4>
&lt;p>This is dreadful:&lt;/p>
&lt;pre>&lt;code class="language-python">s = 50000
&lt;/code>&lt;/pre>
&lt;p>This is better:&lt;/p>
&lt;pre>&lt;code class="language-python">salary = 50000
&lt;/code>&lt;/pre>
&lt;p>But this is best:&lt;/p>
&lt;pre>&lt;code class="language-python">base_salary= 50000
&lt;/code>&lt;/pre>
&lt;h4 id="ijk-and-friends-are-only-for-indices">i,j,k (and Friends) Are Only For Indices&lt;/h4>
&lt;p>Let&amp;rsquo;s say you want to iterate through a list of office locations for a Texas-based company:&lt;/p>
&lt;pre>&lt;code class="language-python">office_locations = [&amp;quot;Houston&amp;quot;, &amp;quot;Austin&amp;quot;, &amp;quot;Dallas&amp;quot;]
&lt;/code>&lt;/pre>
&lt;p>This is awful. &lt;code>i&lt;/code> adds no value here; it can only confuse you.&lt;/p>
&lt;pre>&lt;code class="language-python">for i in office_locations:
pass
&lt;/code>&lt;/pre>
&lt;p>This is vaguely acceptable, but only if you know that (1) enumerate returns indices and (2) &lt;code>i&lt;/code> is a conventional name for indices&lt;/p>
&lt;pre>&lt;code class="language-python">for i, location in enumerate(office_locations):
pass
&lt;/code>&lt;/pre>
&lt;p>These two options are likely the best. These variable names help communicate the intention of the code (to iterate through locations) and even make the semantics of &amp;ldquo;enumerate&amp;rdquo; clearer.&lt;/p>
&lt;pre>&lt;code class="language-python">for location in office_locations:
pass
for loc_index, location in enumerate(office_locations):
pass
&lt;/code>&lt;/pre>
&lt;h4 id="dont-add-types-if-they-dont-add-information">Don&amp;rsquo;t Add Types If They Don&amp;rsquo;t Add Information&lt;/h4>
&lt;p>Appending type information to variables is usually a poor idea.
Variable names can lie &amp;ndash; there&amp;rsquo;s nothing to stop a variable named &lt;code>this_is_totally_a_list&lt;/code> from being (or becoming!) something other than a list.
So, don&amp;rsquo;t add typing information through variable names. Instead, use your language&amp;rsquo;s type system (or type hint system!) &amp;ndash; it&amp;rsquo;s there for a reason.&lt;/p>
&lt;p>These variables, for example, are probably poorly named:&lt;/p>
&lt;pre>&lt;code class="language-python">employee_name_string = &amp;quot;Robin&amp;quot;
employee_name_list = [&amp;quot;Robin&amp;quot;, &amp;quot;Sam&amp;quot;, &amp;quot;Alex&amp;quot;]
&lt;/code>&lt;/pre>
&lt;h4 id="make-sure-you-can-pronounce-it">Make Sure You Can Pronounce It&lt;/h4>
&lt;p>If you need a variable for a company street address, don&amp;rsquo;t do this:&lt;/p>
&lt;pre>&lt;code class="language-python">cmpy_strt_adr = &amp;quot;100 Foo Street&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>No one wants to stumble through pronouncing &amp;ldquo;cumpy stert adder&amp;rdquo;.
Be kind to yourself and your coworkers &amp;ndash; create names that aren&amp;rsquo;t torturous to pronounce.&lt;/p>
&lt;h4 id="variables-and-classes-are-nouns-functions-and-methods-are-verbs">Variables and Classes are Nouns. Functions and Methods are Verbs.&lt;/h4>
&lt;p>Classes model things; methods model actions. Nouns represent things; verbs represent actions.
There&amp;rsquo;s a natural division here, and you should respect it. Use nouns to name your classes; use verbs to name your methods.&lt;/p>
&lt;p>Don&amp;rsquo;t do this:&lt;/p>
&lt;pre>&lt;code class="language-python">class GetEmployeeInfo(object):
def years_of_service_calculator(self):
pass
&lt;/code>&lt;/pre>
&lt;p>Instead, do this:&lt;/p>
&lt;pre>&lt;code class="language-python">class Employee(object): # GOOD!
def get_years_of_service(self): # GOOD!
pass
&lt;/code>&lt;/pre>
&lt;h4 id="pick-your-conventions-and-stick-with-them">Pick Your Conventions and Stick with Them&lt;/h4>
&lt;p>If you use snake_case for methods, use snake_case. If you prefer camelCase, use camelCase.&lt;/p>
&lt;p>If you like using the &amp;ldquo;get&amp;rdquo; prefix, that&amp;rsquo;s great &amp;ndash; don&amp;rsquo;t stray into &amp;ldquo;fetch&amp;rdquo; or &amp;ldquo;acquire&amp;rdquo; unless you have an &lt;em>very&lt;/em> convincing use-case.&lt;/p>
&lt;p>Inconsistent conventions magnify the cognitive burden of reading code; consistent conventions ease that burden.
This might &lt;em>seem&lt;/em> trivial, but I assure you it is not. Programming is hard enough without cognitive self-sabotage.&lt;/p>
&lt;p>So, definitely don&amp;rsquo;t do this:&lt;/p>
&lt;pre>&lt;code class="language-python">class Employee(object):
def getName(self):
pass
def fetch_title(self):
pass
def Return-Salary(self):
pass
def aCqQiReMaNaGeR(self):
pass
&lt;/code>&lt;/pre>
&lt;h2 id="act-ii-functions-or-hermia-would-make-a-good-function">Act II: Functions. Or, Hermia Would Make a Good Function&lt;/h2>
&lt;p>In &lt;em>A Midsummer Night&amp;rsquo;s Dream&lt;/em>, Helena says of Hermia&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>And though she be but little, she is fierce.&lt;a href="#dream">[3]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Just the qualities we want in a function: little and fierce.&lt;/p>
&lt;p>Programs made up of small, powerful functions are easier to understand, easier to test, and easier to maintain.&lt;/p>
&lt;p>Check out this big ol' function. No need to get too involved with it. It is, after all, fake. But glance through it and get the gist.&lt;/p>
&lt;pre>&lt;code class="language-python">def promote_employee(employee, new_title):
# give the employee his new title, adding 'Senior' or 'Junior' if necessary
if employee.years_of_service &amp;gt; 10:
fully_udated_title = &amp;quot;Senior &amp;quot; + new_title
elif employee.years_of_service &amp;lt; 2:
fully_udated_title = &amp;quot;Junior &amp;quot; + new_title
employee.title = fully_udated_title
# compute the employee's new salary
new_salary_bracket_min, new_salary_bracket_max = human_resources.get_salary_bracket(new_title)
if employee.salary &amp;lt;= new_salary_bracket_min:
new_salary = new_salary_bracket_min
elif employee.salary &amp;gt;= new_salary_bracket_max:
new_salary = new_salary_bracket_max
else:
new_salary = ( new_salary_bracket_min + new_salary_bracket_max ) / 2.0
employee.salary = new_salary
# compute the employee's new 401k matching
if human_resources.is_role_401k_eligible(new_title):
new_max_401k_matching = human_resources.get_max_401k_matching(new_title)
min_401k_matching = human_resources.get_company_minimum_401k_matching()
if employee.matching_401k == 0:
new_matching_401k = min_401k_matching
else:
new_matching_401k = min(new_max_401k_matching, employee.matching401k + 0.01)
else:
new_matching401k = 0
employee.matching_401k = new_matching_401k
# compute the new stock compensation
stock_level_1, stock_level_2, stock_level_3 = human_resources.get_stock_levels(new_title)
if employee.tenure &amp;gt; 20:
new_stock_total = stock_level_3
elif employee.tenure &amp;gt; 10:
new_stock_total = stock_level_2
else:
new_stock_total = stock_level_1
employee.stock = new_stock_level
return employee
&lt;/code>&lt;/pre>
&lt;p>That&amp;rsquo;s one &lt;em>long&lt;/em> function. It isn&amp;rsquo;t terribly easy to read, the logic of each individual part obfuscates the function&amp;rsquo;s larger goal, and there are control-flow statements everywhere. The comments help &lt;em>a little&lt;/em>, but they shouldn&amp;rsquo;t be necessary.&lt;/p>
&lt;p>In short, this function isn&amp;rsquo;t short, and it isn&amp;rsquo;t clear.&lt;/p>
&lt;p>Compare it with these beautiful little functions:&lt;/p>
&lt;pre>&lt;code class="language-python">def promote_employee(employee, new_title):
update_title(employee, new_title)
update_salary(employee, new_title)
update_401k_matching(employee, new_title)
update_stock(employee, new_title)
return employee
def update_title(employee, new_title):
if employee.years_of_service &amp;gt; 10:
new_title = &amp;quot;Senior &amp;quot; + new_title
elif employee.years_of_service &amp;lt; 2:
new_title = &amp;quot;Junior &amp;quot; + new_title
employee.title = new_title
# imagine that the rest of the new &amp;quot;update&amp;quot; functions are similarly defined below
&lt;/code>&lt;/pre>
&lt;p>Now &lt;em>that&lt;/em> is readable!&lt;/p>
&lt;p>It&amp;rsquo;s immediately clear, even at a glance, what is involved in promoting an employee. The details of updating title, salary, 401k, and stock no longer obfuscate the larger purpose of &lt;code>promote_employee&lt;/code>. If anyone cares about the finer details of, say, title updating, they can go look in &lt;code>update_title&lt;/code>.&lt;/p>
&lt;p>There are three tricks here: (1) functions should be small, (2) do only one thing, and (3) operate at a single level of abstraction. The next sections cover each trick in detail.&lt;/p>
&lt;h3 id="function-trick-1-functions-should-be-small">Function Trick #1: Functions Should Be Small&lt;/h3>
&lt;p>To quote Robert C. Martin on it,&lt;/p>
&lt;blockquote>
&lt;p>The first rule of functions is that they should be small. The second rule of functions is that
&lt;em>they should be smaller than that&lt;/em>. &lt;a href="#clean2">[5]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Very clever. But how small is &amp;ldquo;smaller than that?&amp;rdquo;&lt;/p>
&lt;p>Honestly, there isn&amp;rsquo;t a universally-accepted measure of &amp;ldquo;small.&amp;rdquo; Here are some statistics from a Ruby codebase written by Martin Fowler. Fowler is something of a thought-leader in software engineering theory, so one could do worse than emulating him.&lt;/p>
&lt;ul>
&lt;li>59% of his functions are less than three lines&lt;/li>
&lt;li>75% of his functins are less than five lines&lt;/li>
&lt;li>93% of his functions are less than ten lines &lt;a href="#function">[6]&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Now those are some small functions! Granted, Fowler is both (1) particularly devoted to the idea of small functions and (2) writing in Ruby, a particularly terse language.
Your definition of small need not be the same as Fowler&amp;rsquo;s; you should treat these numbers as shaggy heuristics rather than a statements of Truth.&lt;/p>
&lt;p>As a general rule, if your functions start to exceed 10 lines, you should take a step back and ask yourself: does this function &lt;em>really&lt;/em> need to be this big?
The answer might very well be &amp;ldquo;Yes!&amp;rdquo;, but it&amp;rsquo;s probably &amp;ldquo;No&amp;hellip;&amp;rdquo;&lt;/p>
&lt;p>Luckily, if you follow trick #2, your functions will naturally remain small.&lt;/p>
&lt;h3 id="function-trick-2-functions-should-do-one-thing">Function Trick #2: Functions Should Do One Thing&lt;/h3>
&lt;p>To quote Robert C. Martin again,&lt;/p>
&lt;blockquote>
&lt;p>Functions should do one thing. They should do it well. They should do it only. &lt;a href="#clean3">[7]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>This, of course, begs the question: how does one know if a function is doing only one thing?&lt;/p>
&lt;p>This is relatively straightforward: try to extract out another meaningful function &amp;ndash; if you&amp;rsquo;re successful, then your function was doing more than one thing. To illustrate this, take another look at &lt;code>promote_employee&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-python">def promote_employee(employee, new_title):
update_title(employee, new_title)
update_salary(employee, new_title)
update_401k_matching(employee, new_title)
update_stock(employee, new_title)
return employeee
&lt;/code>&lt;/pre>
&lt;p>Keeping in mind the idea that functions should do only one thing, is there another function that could be extracted from &lt;code>promote_employee&lt;/code>?&lt;/p>
&lt;p>There&amp;rsquo;s an argument to be made that &lt;code>update_salary&lt;/code>, &lt;code>update_401k_matching&lt;/code>, and &lt;code>update_stock&lt;/code> might be extracted into a new function: &lt;code>update_compensation&lt;/code>.
This new function does one thing &amp;ndash; it updates an employees compensation.&lt;/p>
&lt;p>Take a look:&lt;/p>
&lt;pre>&lt;code class="language-python">def promote_employee(employee, new_title):
update_title(employee, new_title)
updated_compensation(employee, new_title)
return employee
def update_compensation(employee, new_title):
update_salary(employee, new_title)
update_401k_matching(employee, new_title)
update_stock(employee, new_title)
&lt;/code>&lt;/pre>
&lt;p>Now those are some good looking functions! Try as you might, there just doesn&amp;rsquo;t seem to be any way of extracting additional meaningful functions from either &lt;code>promote_employee&lt;/code> or &lt;code>update_compensation&lt;/code>. These functions have been divided up into their smallest reasonable logical components, and as a result, they are small, readable, easy to test, and easy to maintain.&lt;/p>
&lt;h3 id="function-trick-3-functions-should-operate-at-a-single-level-of-abstraction">Function Trick #3: Functions Should Operate at a Single Level of Abstraction&lt;/h3>
&lt;p>This final function trick is, unsurprisingly, a little more abstract. What exactly is a &amp;ldquo;level of abstraction&amp;rdquo;? And why should functions only operate at one of them?&lt;/p>
&lt;p>Put simply, a level of abstraction is a level of detail. High levels of abstraction are light on details; low levels of abstraction of heavy on details.&lt;/p>
&lt;p>Consider &lt;code>update_compensation&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-python">def update_compensation(employee, new_title):
update_salary(employee, new_title)
update_401k_matching(employee, new_title)
update_stock(employee, new_title)
&lt;/code>&lt;/pre>
&lt;p>This function operates at a relatively high level of abstraction &amp;ndash; it&amp;rsquo;s concerned with &lt;em>what&lt;/em> updating compensation entails, not with &lt;em>the details&lt;/em> of how compensation updates are carried out. If, for example, you decided to include the specific details of &lt;code>update_stock&lt;/code> in &lt;code>update_compensation&lt;/code>, you would end up with a rather muddled looking &lt;code>update_compensation&lt;/code> function:&lt;/p>
&lt;pre>&lt;code class="language-python">def update_compensation(employee, new_title):
update_salary(employee, new_title)
update_401k_matching(employee, new_title)
stock_level_1, stock_level_2, stock_level_3 = human_resources.get_stock_levels(new_title)
if employee.tenure &amp;gt; 20:
new_stock_total = stock_level_3
elif employee.tenure &amp;gt; 10:
new_stock_total = stock_level_2
else:
new_stock_total = stock_level_1
employee.stock = new_stock_level
&lt;/code>&lt;/pre>
&lt;p>&lt;code>update_compensation&lt;/code> is now operating at two levels of abstraction, two levels of detail. It contains high-abstraction, low-detail salary and 401k updates.
But it also contains a very low-abstraction, high-detail stock update. This update just doesn&amp;rsquo;t &lt;em>fit&lt;/em>. It makes &lt;code>update_compensation&lt;/code> harder to read, harder to describe, and harder to understand in the context of the employee-promoting process.&lt;/p>
&lt;p>So after you code up a function, ask yourself if it&amp;rsquo;s operating at a single level of abstaction. If it isn&amp;rsquo;t, you&amp;rsquo;ve likely discovered an excellent opportunity to make your code easier to read, explain, and maintain.&lt;/p>
&lt;h2 id="act-iii-come-hither-come-hither-how-did-this-argument-begin-or-arguments-confuse-don-adriano-and-everyone-else">Act III: &lt;strong>&amp;ldquo;Come hither, come hither. How did this argument begin?&amp;rdquo; Or, Arguments Confuse Don Adriano (And Everyone Else)&lt;/strong>&lt;/h2>
&lt;p>Arguments complicate your code. There&amp;rsquo;s no way around it.&lt;/p>
&lt;p>Arguments make testing, understanding, and debugging code more difficult, and that difficulty grows &lt;em>exponentially&lt;/em> in the number of arguments.&lt;/p>
&lt;p>Naturally, our friend Robert C. Martin agrees:&lt;/p>
&lt;blockquote>
&lt;p>The ideal number of arguments for a function is
zero (niladic). Next comes one (monadic), followed
closely by two (dyadic). Three arguments (triadic)
should be avoided where possible. More than three
(polyadic) requires very special justification — and
then shouldn’t be used anyway. &lt;a href="#clean4">[9]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Martin takes a pretty hard line against arguments. There are, in fact, some excellent reasons to write functions with more than three functions.
Heavily-used public-facing objects (like Python&amp;rsquo;s &lt;a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html" target="_blank" rel="noopener">pandas dataframe&lt;/a>) intentionally heap loads of functionality into a fantastically convenient interface that necessarily requires a larger number of arguments.&lt;/p>
&lt;p>But! Martin is right in the &lt;em>general&lt;/em> case. Testability, readability, and maintainability do tend to decrease as the number of arguments increases.
Accordingly, you should do your best to limit the number of arguments in your functions and classes.&lt;/p>
&lt;p>Here are some tricks for tamping down your arguments!&lt;/p>
&lt;h3 id="argument-trick-1-make-your-arguments-classy">Argument Trick #1: Make Your Arguments Classy&lt;/h3>
&lt;p>You might be writing a function like this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">def manipulate_3D_point(x, y, z):
# Do something nifty with a point sitting in three dimensions.
pass
&lt;/code>&lt;/pre>
&lt;p>Any function that needs to know about a point sitting in 3D space will need &lt;em>at least&lt;/em> three arguments: x, y, and z. But that&amp;rsquo;s not a stellar start to writing functions with only a few arguments.&lt;/p>
&lt;p>What&amp;rsquo;s a programmer to do?&lt;/p>
&lt;p>Well, since &lt;code>x&lt;/code>, &lt;code>y&lt;/code>, and &lt;code>z&lt;/code> are all necessary to make a 3D point, they can (and should!) be bound up in a 3D point class!&lt;/p>
&lt;pre>&lt;code class="language-python">class Point_3D(object):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def manipulate_3D_point( a_3D_point ):
# Do something nifty with something a point sitting in three dimensions.
pass
&lt;/code>&lt;/pre>
&lt;p>&lt;em>Much&lt;/em> better! You&amp;rsquo;re function has all the information it needs, you&amp;rsquo;ve minimized your argument list, and I&amp;rsquo;d even argue that you added significant clarity to your code by adding a simple little class. (Note: for Pythonic fun with classes that simply bind data together, check out Python&amp;rsquo;s &lt;a href="https://docs.python.org/3/library/collections.html#collections.namedtuple" target="_blank" rel="noopener">namedtuple&lt;/a>.&lt;/p>
&lt;p>Granted, this example is pretty obvious. But the point remains (pun intended): when your argument list starts to grow, some of your arguments are likely related enough to make a coherent class.&lt;/p>
&lt;h3 id="argument-trick-2-make-sure-arguments-dont-come-out-of-functions">Argument Trick #2: Make Sure Arguments Don&amp;rsquo;t Come OUT of Functions&lt;/h3>
&lt;p>Arguments are confusing enough. But arguments that come &lt;em>out&lt;/em> of functions are even worse.&lt;/p>
&lt;p>At this point you should be a little confused. Don&amp;rsquo;t arguments go &lt;em>into&lt;/em> functions by definition? Why yes. They do. But if you&amp;rsquo;re not careful, they can surprise you (and everyone else) by coming back &lt;em>out&lt;/em> of your functions. Let me illustrate.&lt;/p>
&lt;p>Imagine that you&amp;rsquo;re reading some (icky) code, and you stumble across this little gem:&lt;/p>
&lt;pre>&lt;code class="language-python">text = extend_text( text, extend_value )
&lt;/code>&lt;/pre>
&lt;p>This line of code looks like it probably&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>Takes a text and another value as arguments&lt;/li>
&lt;li>Extends the text using &lt;code>extend_value&lt;/code>&lt;/li>
&lt;li>&amp;hellip;and then returns text again.&lt;/li>
&lt;/ul>
&lt;p>So text goes &lt;em>in&lt;/em>, and then comes back &lt;em>out&lt;/em>. Yuck.&lt;/p>
&lt;p>This works significantly better as&lt;/p>
&lt;pre>&lt;code class="language-python">text.extend( value )
&lt;/code>&lt;/pre>
&lt;p>Ah, much better! &lt;code>value&lt;/code> goes &lt;em>in&lt;/em>, &lt;code>text&lt;/code> comes &lt;em>out&lt;/em>, and no one is surprised.&lt;/p>
&lt;h3 id="argument-trick-3-flags-are-for-countries-not-for-programmers">Argument Trick #3: Flags are for Countries, Not for Programmers&lt;/h3>
&lt;p>Programmers &lt;em>love&lt;/em> passing flags to functions.&lt;/p>
&lt;p>Like so&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">class SomeCrazyData(object):
def output_data(self, as_CSV):
if as_CSV:
# Output the data as CSV
return some_CSV
else:
# Output the data as JSON
return some_JSON
&lt;/code>&lt;/pre>
&lt;p>Look how simple! Now you can get &lt;code>output_data&lt;/code> to return the data in CSV &lt;em>or&lt;/em> JSON format. All you have to do is pass a boolean flag and WHAM! You&amp;rsquo;re done!&lt;/p>
&lt;p>But this function makes me feel really uneasy.&lt;/p>
&lt;ul>
&lt;li>&lt;code>as_CSV&lt;/code> is a boolean. If its value is &lt;code>True&lt;/code>, then it returns a CSV. If its value is the &lt;em>opposite&lt;/em> of &lt;code>True&lt;/code>, then it returns&amp;hellip; Uh&amp;hellip; Well I guess it returns the opposite of a CSV. Whatever that is. I haven&amp;rsquo;t the foggiest idea.&lt;/li>
&lt;li>Functions do one thing! But&amp;hellip; This function does two things. One for the &lt;code>True&lt;/code> case and another for the &lt;code>False&lt;/code> case. That&amp;rsquo;s unfortunate.&lt;/li>
&lt;li>The function&amp;rsquo;s name and arguments don&amp;rsquo;t tell me what I can use the function for! To figure that out, I have to rely on good documentation (Ahahahahaha! Yeah right&amp;hellip;) or look around inside the function until I figure it out.&lt;/li>
&lt;/ul>
&lt;p>To recap, we have a function with an ambiguous argument that does two things without making it clear from its definition what those two things are.&lt;/p>
&lt;p>Yuck.&lt;/p>
&lt;p>It&amp;rsquo;s a much better idea to do something like this:&lt;/p>
&lt;pre>&lt;code class="language-python">class SomeCrazyData(object):
def output_data_as_CSV(self):
# Output the data as CSV
return some_CSV
def output_data_as_JSON(self):
# Output the data as JSON
return some_JSON
&lt;/code>&lt;/pre>
&lt;p>Do yourself a favor: don&amp;rsquo;t use flags unless you &lt;em>absolutely&lt;/em> have to.&lt;/p>
&lt;h3 id="argument-trick-4-leave-none-alone">Argument Trick #4: Leave &amp;ldquo;None&amp;rdquo; Alone!&lt;/h3>
&lt;p>None is so tempting. But it&amp;rsquo;s usually a sub-optimal idea.&lt;/p>
&lt;p>It is a value that represents the absence of any value. And that alone should make you feel weird.&lt;/p>
&lt;p>You cannot iterate through None. You cannot cannot use it as a function. It has no attributes, and it has no methods. All None will ever do for you is raise the exception &amp;ldquo;AttributeError: &amp;lsquo;NoneType&amp;rsquo; object has no attribute&amp;hellip;&amp;rdquo;&lt;/p>
&lt;p>If you return it from a function, it will force every caller of your function to perform None-checks. If they forget a check, they risk an AttributeError.&lt;/p>
&lt;p>If you pass it into a function, (especially as a default argument), you&amp;rsquo;ll obfuscate your logic with None-checks and doom your helper functions to the same fate.&lt;/p>
&lt;p>And yet, None still gets used because it is convenient. It has no features: all you can do with it is ask &amp;ldquo;Are you None?&amp;rdquo;&lt;/p>
&lt;p>Given its simple featurelessness, folks use None to store all kinds of random information: Did I ever set this variable? Did I call this function? Did I succeed in some task? &lt;em>All&lt;/em> sorts of things.&lt;/p>
&lt;p>They&amp;rsquo;re all a mistake. There is &lt;em>always&lt;/em> a better way to signal your intention than with a None.&lt;/p>
&lt;p>You could&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>Raise an exception (potenially in a try-except block) to signal that something invalid happened&lt;/li>
&lt;li>Ditch the default argument. If an argument doesn&amp;rsquo;t have a sensible default, then why are you trying to force one on it?&lt;/li>
&lt;li>Use a sensible, non-mutable default: a custom &amp;ldquo;empty&amp;rdquo; class or an enum.&lt;/li>
&lt;li>Re-architect your functions and logic so that they don&amp;rsquo;t need None. Your code will almost certainly be more robust as a result.&lt;/li>
&lt;/ul>
&lt;p>Just try not to use None.&lt;/p>
&lt;p>If you&amp;rsquo;re still not convinced, consider for a moment that Turing award-winning computer scientist Tony Hoare &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare" target="_blank" rel="noopener">believes that None (aka: the null refernece) is a mistake&lt;/a>. And he invented it.&lt;/p>
&lt;h2 id="act-iv-forgive-the-comment-that-my-passion-made--upon-thy-feature-11john-or-king-john-regrets-commenting-and-you-should-too">Act IV: &amp;ldquo;Forgive the comment that my passion made / Upon thy feature&amp;rdquo; &lt;a href="#john">[11]&lt;/a> Or, King John Regrets Commenting and You Should Too&lt;/h2>
&lt;p>Comments are weird.&lt;/p>
&lt;p>Countless programmers extol the virtues &amp;ldquo;well-commented&amp;rdquo; code, but no two people ever seem to have the same idea of what &amp;ldquo;well-commented&amp;rdquo; means. Now why is that? As you might have come to expect, we&amp;rsquo;re going to turn to Robert C. Martin for the answer:&lt;/p>
&lt;blockquote>
&lt;p>The proper use of comments is to compensate for our failure to express ourself in
code. Note that I used the word
failure. I meant it. Comments are always failures. &lt;a href="#clean5">[12]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>It is worth repeating: comments are always failures. We fail to express ourselves clearly in code, and so we leave a comment behind to mark that failure. Over time, that comment slowly forgets its purpose as the surrounding system evolves. And then one day, the comment looks up and realizes that it no longer means anything to anyone. It has no purpose. All it can do is wait to be deleted or replaced.&lt;/p>
&lt;p>Almost brings a tear to your eye, doesn&amp;rsquo;t it?&lt;/p>
&lt;p>If only our programming languages were more expressive &amp;ndash; if only &lt;em>we&lt;/em> were more expressive!
But they aren&amp;rsquo;t, and we&amp;rsquo;re not. So there are still a few kinds of neccesary (but necessarily evil!) comments.&lt;/p>
&lt;h3 id="dire-warnings">Dire Warnings&lt;/h3>
&lt;pre>&lt;code class="language-python"># DONT CHANGE THIS NUMBER. If it isn't EXACTLY equal to 4, then - believe it or not - the San Andreas Fault will violently hemorrhage
# and cause San Francisco to topple helplessly into the Pacific.
b = 4
&lt;/code>&lt;/pre>
&lt;p>Occasionally, things like this happen. Systems aren&amp;rsquo;t always as robust as we&amp;rsquo;d like them, and programmers who&amp;rsquo;ve erred leave warnings behind. They&amp;rsquo;re often valuable, and not to be dismissed without investigation.&lt;/p>
&lt;h3 id="explaining-interfaces-you-dont-own">Explaining Interfaces You Don&amp;rsquo;t Own&lt;/h3>
&lt;pre>&lt;code class="language-python"># This only works if you pass in the username in Pig Latin...
fetch_BookFace_messages(username=&amp;quot;odycay anzandtvay&amp;quot;)
&lt;/code>&lt;/pre>
&lt;p>We hope that all public APIs are well-documented and entirely unastonishing. But that&amp;rsquo;s just not the case. Ocassioanlly, Alice-in-Wonderland-esque functions climb out of the rabbit hole and make their way into our systems. We can sputter and swear and jump up and down all we&amp;rsquo;d like, but there&amp;rsquo;s often very little we can do to change wonky code that we don&amp;rsquo;t own. All we can do is leave a comment and hope for the best.&lt;/p>
&lt;p>Admittedly, the wonky code might one day change and our comment will become unnecessary. But &amp;lsquo;til then, a few words might save our colleagues loads of time and energy.&lt;/p>
&lt;h3 id="explaining-the-truly-esoteric">Explaining the Truly Esoteric&lt;/h3>
&lt;p>Occasionally, you&amp;rsquo;ll find yourself coding in the footsteps of someone with a skill set very different from your own. That previous coder might be, for example, a math PhD. And although she has long left the firm, she did provide a nice equation to help you understand her code:&lt;/p>
&lt;p>\begin{align*}
&amp;amp;a=12+7 \int_0^2
\left(
-\frac{1}{4}\left(e^{-4t_1}+e^{4t_1-8}\right)
\right)&lt;br>
\end{align*}&lt;/p>
&lt;p>Alas, your degree isn&amp;rsquo;t in math, so you don&amp;rsquo;t haveno hope of making sense of this.&lt;/p>
&lt;p>&lt;em>However!&lt;/em>&lt;/p>
&lt;p>Your erstwhile colleague also provided a nice, English description of this equation&amp;rsquo;s purpose and implementation in a comment:&lt;/p>
&lt;pre>&lt;code class="language-python"># Here's exactly what the equation does and why...
# &amp;lt;insert clear explanation here&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>Now you can impress your fellow developers at the next meeting with your lucid descriptions of complex computations.&lt;/p>
&lt;p>Hurray!&lt;/p>
&lt;h3 id="licenses-test-files-permissions-primary-maintainers-and-other-necessities">Licenses, Test Files, Permissions, Primary Maintainers, and Other Necessities&lt;/h3>
&lt;pre>&lt;code class="language-python">&amp;quot;&amp;quot;&amp;quot;
Primary: Cody VanZandt
Permission Group: Blah Blah and Foo
Tests: /tests/ThingDoer.py
License: Creative Commons 4.0 Attribute
&amp;quot;&amp;quot;&amp;quot;
def a_very_important_function():
# do some very important computational business
&lt;/code>&lt;/pre>
&lt;p>This stuff is pretty important in systems with lots of developers. Some folks might even scrape and mine a large collection of it for its secrets.&lt;/p>
&lt;p>Not &lt;em>all&lt;/em> such file statistics are useful (things like version number are generally best left to version control software), but a lot of them are, so they&amp;rsquo;re ofen worth including.&lt;/p>
&lt;h2 id="act-v-you-are-too-blunt-go-to-it-orderly-or-petruchio-needs-better-formatting">Act V: &amp;ldquo;You are too blunt. Go to it orderly.&amp;rdquo; Or, Petruchio Needs Better Formatting&lt;/h2>
&lt;p>Poor formatting can hamstring your readers just as easily as poor design.&lt;/p>
&lt;p>If no one knows where to look for anything, the task of understanding what&amp;rsquo;s actually happening grows more difficult.&lt;/p>
&lt;p>You (and everyone who reads your code) will be grateful if you spend a litte time formatting your code to be readable and unsurprising.&lt;/p>
&lt;h3 id="how-long-should-a-file-be">How Long Should a File Be?&lt;/h3>
&lt;p>The short answer? I don&amp;rsquo;t know.&lt;/p>
&lt;p>Just long enough and not any longer, I think.&lt;/p>
&lt;p>1000 lines is usually too many, and a single line is usually too few. In truth, file length isn&amp;rsquo;t the sort of thing you should optimize. You should instead ask yourself &amp;ldquo;In one sentence, what is this file doing?&amp;rdquo; If you can&amp;rsquo;t answer that question without a slough of commas and conjuctions, then you&amp;rsquo;re file is too long. See, files - just like functions - should only do one thing.&lt;/p>
&lt;h3 id="where-should-i-put-things">Where Should I Put Things?&lt;/h3>
&lt;p>Ah, now this question is much easier!&lt;/p>
&lt;p>I am, unsurprisingly, a huge fan of Robert C. Martin&amp;rsquo;s answer&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>We would like a source file to be like a newspaper article. The name should be simple
but explanatory. The name, by itself, should be sufficient to tell us whether we are in the
right module or not. The topmost parts of the source file should provide the high-level
concepts and algorithms. Detail should increase as we move downward, until at the end
we find the lowest level functions and details in the source file. &lt;a href="#clean6">[13]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>That is a fantastic simile: a source file is a like a &lt;em>newspaper article&lt;/em>.&lt;/p>
&lt;p>Let&amp;rsquo;s look an example of this done &lt;em>really&lt;/em> wrong:&lt;/p>
&lt;pre>&lt;code class="language-python">def add_report_content(empty_report):
content = get_report_content()
full_report = empty_report + content
return full_report
def make_unformatted_report():
return &amp;quot;An unformatted report&amp;quot;
def format_report(unformatted_report):
return unformatted_report.format()
def make_report_base():
unformatted_report = make_unformatted_report()
return format_report(unformatted_report)
def get_report_content():
return &amp;quot;Some content.&amp;quot;
def make_report():
report_base = make_report_base()
finished_report = add_report_content(report_base)
return finished_report
&lt;/code>&lt;/pre>
&lt;p>How obvious is this program&amp;rsquo;s flow? Can you glance through it and tell me how these functions work together? Which functions depend on which other functions?&lt;/p>
&lt;p>It&amp;rsquo;s not obvious.&lt;/p>
&lt;p>Sure, you can probably look around and figure out that everything starts with &lt;code>make_report&lt;/code>, but the file leads you on a merry chase - up and down, and up and down, and&amp;hellip; Eh. It&amp;rsquo;s a mess.&lt;/p>
&lt;p>Compare with this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">def make_report():
report_base = make_report_base()
finished_report = add_report_content(report_base)
return finished_report
def make_report_base():
unformatted_report = make_unformatted_report()
report_base = format_report(unformatted_report)
return report_base
def make_unformatted_report():
return &amp;quot;An unformatted report&amp;quot;
def format_report(unformatted_report):
return unformatted_report.format()
def add_report_content(empty_report):
content = get_report_content()
full_report = empty_report + content
return full_report
def get_report_content():
return &amp;quot;Some content.&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>Now that&amp;rsquo;s &lt;em>much&lt;/em> better!&lt;/p>
&lt;p>What does this file do? It&amp;rsquo;s right at the top: it makes a report with &lt;code>make_report&lt;/code>.&lt;/p>
&lt;p>You can follow the logic of &lt;code>make_report&lt;/code> first through &lt;code>make_report_base&lt;/code> and then through &lt;code>add_report_content&lt;/code>. Each of which is located (along with its dependent functions) in order right below &lt;code>make_report&lt;/code>.&lt;/p>
&lt;p>It&amp;rsquo;s just like a well structured essay:&lt;/p>
&lt;ul>
&lt;li>Thesis (&lt;code>make_report&lt;/code>)
&lt;ul>
&lt;li>Topic 1 (&lt;code>make_report_base&lt;/code>)
&lt;ul>
&lt;li>Support for Topic 1 (&lt;code>make_unformatted_report&lt;/code>)&lt;/li>
&lt;li>Support for Topic 1 (&lt;code>format_report&lt;/code>)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Topic 2 (&lt;code>add_report_content&lt;/code>)
&lt;ul>
&lt;li>Support for Topic 2 (`get_report_content)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="do-not-line-up-your-assignments">Do Not Line Up Your Assignments&lt;/h3>
&lt;p>Lastly, a minor and perhaps controversial suggestion.&lt;/p>
&lt;p>Let&amp;rsquo;s say you have a big ol&amp;rsquo; load of assignment statements&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">def some_function():
a_second_long_variable = 5
short_var = 1
an_extremely_long_variable_name = 6
longer_variable = 3
also_short = 2
an_even_longer_variable = 4
return &amp;quot;Boo! Terrible!&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>So you think to yourself,&lt;/p>
&lt;p>&amp;ldquo;Gee, those staggered assignment statements sure look ugly. Let me fix that up&amp;hellip;&amp;rdquo;&lt;/p>
&lt;pre>&lt;code class="language-python">def some_function():
a_second_long_variable = 5
short_var = 1
an_extremely_long_variable_name = 6
longer_variable = 3
also_short = 2
an_even_longer_variable = 4
return &amp;quot;Boo! Terrible!&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>Ah! That&amp;rsquo;s much better!&lt;/p>
&lt;p>Except&amp;hellip; it isn&amp;rsquo;t better at all. It&amp;rsquo;s worse.&lt;/p>
&lt;p>Firstly, you missed the point. The problem isn&amp;rsquo;t that loads of staggered assignments right in a row look ugly. The problem is that you have loads of staggered assignments. Don&amp;rsquo;t try to hide that issue under a fresh coat of paint; fix it.&lt;/p>
&lt;p>Secondly, you forgot what assignment statements are really about. You&amp;rsquo;ve drawn the eye away from the assignments and towards the giant list of values. But &lt;em>assignment&lt;/em> is the important thing here, not the giant list of values.&lt;/p>
&lt;p>And finally, this non-traditional assignment plays tricks on the mind. If I didn&amp;rsquo;t know better, I &lt;em>might&lt;/em> think for a crazy second that &lt;code>short_var&lt;/code> is equal to a large amount of whitespace followed by a &lt;code>1&lt;/code>.&lt;/p>
&lt;p>Weird.&lt;/p>
&lt;h2 id="act-vi-doth-not-the-object-cheer-your-heart-my-lord-or-queen-margaret-loves-object-oriented-programming">Act VI: &amp;ldquo;Doth not the object cheer your heart, my lord?&amp;rdquo; Or, Queen Margaret Loves Object-Oriented Programming&lt;/h2>
&lt;p>Objects are great; object-oriented programming is &lt;em>fantastic&lt;/em>.&lt;/p>
&lt;p>And while I don&amp;rsquo;t have the space in this series (let alone this section) to read you the riot act on object-oriented programming, I do have enough space to answer two of the most important questions on classes.&lt;/p>
&lt;p>First up&amp;hellip;&lt;/p>
&lt;h3 id="when-should-you-use-a-class">When Should You Use a Class?&lt;/h3>
&lt;p>There are many reasons to use classes!&lt;/p>
&lt;p>Here&amp;rsquo;s a useful (though wildly incomplete) list:&lt;/p>
&lt;ul>
&lt;li>To protect your data from accidental or incorrect manipulation&lt;/li>
&lt;li>To keep data items close to the functions that act on them&lt;/li>
&lt;li>To reduce code reuse&lt;/li>
&lt;li>To model life in code with fidelity&lt;/li>
&lt;li>To provide useful abstractions that make complex systems understandable&lt;/li>
&lt;li>And more!&lt;/li>
&lt;/ul>
&lt;p>But at the end of the day, all of these reasons reduce down to one: you have a rag-tag set of variables (and optionally: functions) that would probably work better if you imposed a little structure.&lt;/p>
&lt;p>So something like this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">def do_something(a, b, c):
pass
def do_something_else(a, b, c):
pass
def do_yet_another_thing(a, b, c):
pass
&lt;/code>&lt;/pre>
&lt;p>Usually works better like this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">class ABC_Thing(object):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def do_something(self):
pass
def do_something_else(self):
pass
def do_yet_another_thing(self):
pass
&lt;/code>&lt;/pre>
&lt;h3 id="how-big-should-classes-be">How Big Should Classes Be?&lt;/h3>
&lt;p>To quote Robert C. Martin one last time,&lt;/p>
&lt;blockquote>
&lt;p>The first rule of classes is that they should be small. The second rule of classes is that &lt;em>they
should be smaller than that&lt;/em>.&lt;a href="#clean7">[15]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>But, we will ask for one last time in this series: just exactly how small &lt;em>is&lt;/em> small?&lt;/p>
&lt;p>Unlike functions, it&amp;rsquo;s not productive to measure size in lines. A class might work with 20 (hopefully small) methods, or with two.&lt;/p>
&lt;p>The only reliable way to corral the size of your classes is to look at how many &lt;em>responsibilities&lt;/em> that class performs.&lt;/p>
&lt;p>Because like functions, a class &lt;em>should only do one thing&lt;/em>.&lt;/p>
&lt;p>For example&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">class Lamp(object):
def turn_on(self):
pass
def turn_off(self):
pass
def convert_to_AC(self, direct_current):
pass
def convert_to_DC(self, alternating_current):
pass
&lt;/code>&lt;/pre>
&lt;p>Now I&amp;rsquo;m no electrician, but I&amp;rsquo;d say that a lamp probably shouldn&amp;rsquo;t know anything about converting different currents. That should probably be in a class called&amp;hellip; uh&amp;hellip; whatever it is you call the thing that converts currents.&lt;/p>
&lt;p>Whatever that thing is called, the point remains the same: you shoulnd&amp;rsquo;t be afraid to split up an overburdened class into a handful of smaller, cleaner classes.&lt;/p>
&lt;h2 id="act-vii-final-words-or-what-you-will">Act VII: Final Words, Or What You Will&lt;/h2>
&lt;p>If you&amp;rsquo;ve made it all the way through &lt;em>Better Coding Through Shakespeare&lt;/em>, I congratulate you. This series weighs in at a few thousand words and includes a fair bit of code. It&amp;rsquo;s not exactly light reading.&lt;/p>
&lt;p>I hope that you&amp;rsquo;ve found this series instructive, but I&amp;rsquo;ll also accept that you believe I am&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;A most notable coward, an infinite and endless liar, an hourly promise breaker, the owner of no one good quality.&amp;rdquo; &lt;a href="#all">[16]&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>And you know, that&amp;rsquo;s fine by me.&lt;/p>
&lt;p>I didn&amp;rsquo;t write this series to proselytize for my particular coding religion. I wrote this because these questions of functions, arguments, comments, formatting, and objects are all worth asking. It doesn&amp;rsquo;t matter that you&amp;rsquo;ve accepted my answers, only that you&amp;rsquo;ve considered the questions.&lt;/p>
&lt;p>Thanks for reading, and good luck out there.&lt;/p>
&lt;p>[ Exit, pursued by a bear. ] &lt;a href="#winter">[17]&lt;/a>&lt;/p>
&lt;h2 id="works-cited">Works Cited&lt;/h2>
&lt;p>&lt;a name="romeo">[1]&lt;/a> Shakespeare, William. &lt;em>Romeo and Juliet.&lt;/em> II.ii&lt;/p>
&lt;p>&lt;a name="clean1">[2]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 18.&lt;/p>
&lt;p>&lt;a name="dream">[3]&lt;/a> Shakespeare, William. &lt;em>A Midsummer Night&amp;rsquo;s Dream.&lt;/em> III.ii&lt;/p>
&lt;p>&lt;a name="mom">[4]&lt;/a> Thanks, Mom!&lt;/p>
&lt;p>&lt;a name="clean2">[5]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 34&lt;/p>
&lt;p>&lt;a name="function">[6]&lt;/a> Fowler, Martin. &lt;a href="https://martinfowler.com/bliki/FunctionLength.html">https://martinfowler.com/bliki/FunctionLength.html&lt;/a>&lt;/p>
&lt;p>&lt;a name="clean3">[7]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 35&lt;/p>
&lt;p>&lt;a name="love">[8]&lt;/a> Shakespeare, William. &lt;em>Love&amp;rsquo;s Labor&amp;rsquo;s Lost.&lt;/em> III.i&lt;/p>
&lt;p>&lt;a name="clean4">[9]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 40&lt;/p>
&lt;p>&lt;a name="john">[10]&lt;/a> Shakespeare, William. &lt;em>King John&lt;/em> IV.iii&lt;/p>
&lt;p>&lt;a name="clean5">[11]&lt;/a>Martin, Robert C. &lt;em>Clean Code.&lt;/em> 54&lt;/p>
&lt;p>&lt;a name="shrew">[12]&lt;/a> Shakespeare, William. &lt;em>The Taming of the Shrew&lt;/em> II.i&lt;/p>
&lt;p>&lt;a name="clean6">[13]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 77&lt;/p>
&lt;p>&lt;a name="henry">[14]&lt;/a> Shakespeare, William. &lt;em>Henry VI&lt;/em> II.&lt;/p>
&lt;p>&lt;a name="clean7">[15]&lt;/a> Martin, Robert C. &lt;em>Clean Code.&lt;/em> 136&lt;/p>
&lt;p>&lt;a name="all">[16]&lt;/a> Shakespeare, William. &lt;em>All&amp;rsquo;s Well That Ends Well&lt;/em> III.VI&lt;/p>
&lt;p>&lt;a name="winter">[17]&lt;/a> Shakespeare, William. &lt;em>Winter&amp;rsquo;s Tale&lt;/em> III.iii&lt;/p></description></item><item><title>The Sorcerer's Code</title><link>https://codyvanzandt.com/blog/the-sorcercers-code/</link><pubDate>Fri, 31 Aug 2018 00:00:00 +0000</pubDate><guid>https://codyvanzandt.com/blog/the-sorcercers-code/</guid><description>&lt;h1 id="were-going-to-need-a-little-magic">We&amp;rsquo;re Going to Need a Little Magic&lt;/h1>
&lt;p>So there you are - mind racing, fingers blurring, the most beautiful Python in all the world coming to life on your screen. You&amp;rsquo;re in the zone!&lt;/p>
&lt;p>Tasks fall to your indomitable will one right after another. Boom! Boom!&lt;/p>
&lt;p>Suddenly, a new task appears on the horizon: you need a class to represent a big ol' hunk of words!&lt;/p>
&lt;p>&amp;ldquo;Ha,&amp;rdquo; you scoff. &amp;ldquo;Easy!&amp;rdquo;&lt;/p>
&lt;p>It takes mere seconds for you to lay out the core of the class.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
&lt;/code>&lt;/pre>
&lt;p>You give the class a try&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
assert hunk.words == [&amp;quot;These&amp;quot;, &amp;quot;are&amp;quot;, &amp;quot;some&amp;quot;, &amp;quot;words&amp;quot;]
&lt;/code>&lt;/pre>
&lt;p>&amp;hellip;and it&amp;rsquo;s flawless, of course. You&amp;rsquo;re an absolute &lt;em>wizard&lt;/em> on the keyboard today!&lt;/p>
&lt;p>Confidently, you print out your new class:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
print(hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>&amp;lt;__main__.WordHunk object at 0x7fb5684d5ef0&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>Oh. Oh no.&lt;/p>
&lt;p>That&amp;rsquo;s so&amp;hellip; ugly!&lt;/p>
&lt;p>You desperately wrack your brain for a solution, but nothing comes to mind. Could you maybe change the print function? Could you do something weird with stdout?&lt;/p>
&lt;p>Ugh - those are terrible ideas.&lt;/p>
&lt;p>You sag in your chair, and your flow state comes to an abrupt end.&lt;/p>
&lt;p>Magician though you may have been, you&amp;rsquo;re going to need some &lt;em>real&lt;/em> magic to get out of this mess.&lt;/p>
&lt;p>Luckily, Python has you covered.&lt;/p>
&lt;p>Allow me to introduce you to&amp;hellip;&lt;/p>
&lt;h3 id="_magic-methods_">&lt;em>&lt;em>Magic Methods!&lt;/em>&lt;/em>&lt;/h3>
&lt;h1 id="the-__init__-method">The &lt;code>__init__&lt;/code> Method&lt;/h1>
&lt;p>You&amp;rsquo;re probably pretty familiar with this little number already.&lt;/p>
&lt;p>By defining &lt;code>__init__&lt;/code>, we can control how class instances are &lt;em>initialized&lt;/em>.&lt;/p>
&lt;p>We used this method just above when we dictated that &lt;code>WordHunk&lt;/code> initializes instances by:&lt;/p>
&lt;ul>
&lt;li>Converting a user-provided string into a list of individual words&lt;/li>
&lt;li>And assigning that new list of words to the instance variable &lt;code>words&lt;/code>&lt;/li>
&lt;/ul>
&lt;h1 id="the-__str__-method">The &lt;code>__str__&lt;/code> Method&lt;/h1>
&lt;p>&lt;code>__str__&lt;/code> allows us to define a &amp;ldquo;human-readable&amp;rdquo; representation of our class. Whatever &lt;code>__str__&lt;/code> returns is what &lt;code>print&lt;/code> will return.&lt;/p>
&lt;p>Armed with this new knowledge, let&amp;rsquo;s fix up &lt;code>WordHunk&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
print(hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>['These', 'are', 'some', 'words']
&lt;/code>&lt;/pre>
&lt;p>Now &lt;em>that&lt;/em> is magical!&lt;/p>
&lt;p>Let&amp;rsquo;s just make sure that WordHunks are &lt;em>always&lt;/em> represented in such a pretty way&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">double_hunk = [hunk, hunk]
print(double_hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>[&amp;lt;__main__.WordHunk object at 0x7fb5684e30b8&amp;gt;, &amp;lt;__main__.WordHunk object at 0x7fb5684e30b8&amp;gt;]
&lt;/code>&lt;/pre>
&lt;p>Oh no. Not again!&lt;/p>
&lt;h1 id="the-__repr__-method">The &lt;code>__repr__&lt;/code> Method&lt;/h1>
&lt;p>Where &lt;code>__str__&lt;/code> fails, &lt;code>__repr__&lt;/code> excels.&lt;/p>
&lt;p>&lt;code>__repr__&lt;/code> allows us to define a &amp;ldquo;machine-readable&amp;rdquo; representation of our class. Whatever &lt;code>__repr__&lt;/code> returns is what will be rendered when we use our class in a data structure or on the command line.&lt;/p>
&lt;p>By convention, the return value of &lt;code>__repr__&lt;/code> should be valid Python code. Ideally, we should be able to recreate an instance of a class &lt;em>from&lt;/em> that instance&amp;rsquo;s &lt;code>__repr__&lt;/code>.&lt;/p>
&lt;p>Let&amp;rsquo;s get to it!&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
double_hunk = [hunk, hunk]
print(double_hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>[WordHunk('These are some words'), WordHunk('These are some words')]
&lt;/code>&lt;/pre>
&lt;p>Perfect! And as a quick double-check, we can get the &lt;code>__repr__&lt;/code> of our objects using Python&amp;rsquo;s &lt;code>repr&lt;/code> function.&lt;/p>
&lt;pre>&lt;code class="language-python">repr(hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>&amp;quot;WordHunk('These are some words')&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>And if we evaluate our &lt;code>__repr__&lt;/code> as if it were valid Python code (because it &lt;em>is&lt;/em>), then we should get back a real Python object:&lt;/p>
&lt;pre>&lt;code class="language-python">eval(repr(hunk))
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>WordHunk('These are some words')
&lt;/code>&lt;/pre>
&lt;p>Ta da! A real &lt;code>WordHunk&lt;/code>!&lt;/p>
&lt;h1 id="the-__len__-method">The &lt;code>__len__&lt;/code> Method&lt;/h1>
&lt;p>Wouldn&amp;rsquo;t it be cool if we could take the length of a &lt;code>WordHunk&lt;/code> using Python&amp;rsquo;s built-in &lt;code>len&lt;/code> function?&lt;/p>
&lt;pre>&lt;code class="language-python">len(hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
&amp;lt;ipython-input-11-d796a42c142a&amp;gt; in &amp;lt;module&amp;gt;()
----&amp;gt; 1 len(hunk)
TypeError: object of type 'WordHunk' has no len()
&lt;/code>&lt;/pre>
&lt;p>Well I sure think it&amp;rsquo;d be cool!&lt;/p>
&lt;p>All we have to do is define the &lt;code>__len__&lt;/code> method. Whatever &lt;code>__len__&lt;/code> returns is how long our &lt;code>WordHunk&lt;/code> will be.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
len(hunk)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>4
&lt;/code>&lt;/pre>
&lt;p>That was pretty easy. It makes sense that the length of a &lt;code>WordHunk&lt;/code>is equal to how many words it contains.&lt;/p>
&lt;h1 id="the-__contains__-method">The &lt;code>__contains__&lt;/code> Method&lt;/h1>
&lt;p>I think it would also be pretty nifty if we could check if a word is in a &lt;code>WordHunk&lt;/code>.&lt;/p>
&lt;p>Something like this:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
&amp;quot;some&amp;quot; in hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
&amp;lt;ipython-input-14-954425367c9d&amp;gt; in &amp;lt;module&amp;gt;()
1 hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
----&amp;gt; 2 &amp;quot;some&amp;quot; in hunk
TypeError: argument of type 'WordHunk' is not iterable
&lt;/code>&lt;/pre>
&lt;p>To get this sort of behavior from our WordHunk, the simplest thing to do is define the &lt;code>__contains__&lt;/code> method.&lt;/p>
&lt;p>&lt;code>__contains__&lt;/code> should take an additional argument (the item in question) and then return a boolean depending on whether or not that item is in our &lt;code>WordHunk&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
&amp;quot;some&amp;quot; in hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>True
&lt;/code>&lt;/pre>
&lt;h1 id="the-__getitem__-method">The &lt;code>__getitem__&lt;/code> Method&lt;/h1>
&lt;p>Here&amp;rsquo;s the next awesome feature I think we should add to &lt;code>WordHunk&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
hunk[0] # Should return: &amp;quot;These&amp;quot;
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
&amp;lt;ipython-input-17-2a1dee428c19&amp;gt; in &amp;lt;module&amp;gt;()
1 hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
----&amp;gt; 2 hunk[0] # Should return: &amp;quot;These&amp;quot;
TypeError: 'WordHunk' object does not support indexing
&lt;/code>&lt;/pre>
&lt;p>To get &lt;code>WordHunk&lt;/code> to support indexing, we need to define &lt;code>__getitem__&lt;/code>.&lt;/p>
&lt;p>&lt;code>__getitem__&lt;/code> should take an additional argument (a index of some sort) and returns the word located at that index.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
hunk[0]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>'These'
&lt;/code>&lt;/pre>
&lt;h1 id="the-__setitem__-method">The &lt;code>__setitem__&lt;/code> Method&lt;/h1>
&lt;p>Now that we can get an item using bracket-notation, it would be nice if we could &lt;em>set&lt;/em> an item using bracket notation.&lt;/p>
&lt;p>Something like this:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
hunk[2] = &amp;quot;different&amp;quot;
hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
&amp;lt;ipython-input-20-0fdc933bef70&amp;gt; in &amp;lt;module&amp;gt;()
1 hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
----&amp;gt; 2 hunk[2] = &amp;quot;different&amp;quot;
3 hunk
TypeError: 'WordHunk' object does not support item assignment
&lt;/code>&lt;/pre>
&lt;p>No surprises here; we&amp;rsquo;re going to use &lt;code>__setitem__&lt;/code> to implement this feature.&lt;/p>
&lt;p>&lt;code>__setitem__&lt;/code> takes &lt;em>two&lt;/em> arguments:&lt;/p>
&lt;ul>
&lt;li>a key to use as an index&lt;/li>
&lt;li>and a new value to write at that index&lt;/li>
&lt;/ul>
&lt;p>Note: there&amp;rsquo;s really no need for &lt;code>__setitem__&lt;/code> to return anything. I can&amp;rsquo;t think of anything reasonable for it to return.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
hunk[2] = &amp;quot;different&amp;quot;
hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>WordHunk('These are different words')
&lt;/code>&lt;/pre>
&lt;h1 id="the-__iter__-method">The &lt;code>__iter__&lt;/code> Method&lt;/h1>
&lt;p>It&amp;rsquo;d be super useful if we could also iterate through our &lt;code>WordHunk&lt;/code> one word at a time.&lt;/p>
&lt;p>Something like this:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
for word in hunk:
print(word)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>These
are
some
words
&lt;/code>&lt;/pre>
&lt;p>Given how this guide has gone so far, you wouldn&amp;rsquo;t have expected that to work. Turns out, if you define &lt;code>__getitem__&lt;/code>, Python will give you iteration for free. Tricky, tricky!&lt;/p>
&lt;p>But what if you &lt;em>don&amp;rsquo;t&lt;/em> want to define &lt;code>__getitem__&lt;/code>, but you still want iteration?&lt;/p>
&lt;p>Have no fear! Here are the nuts and bolts of how Python implements iteration.&lt;/p>
&lt;p>We need to define &lt;em>two&lt;/em> methods on &lt;code>WordHunk&lt;/code>: &lt;code>__iter__&lt;/code> and &lt;code>__next__&lt;/code>.&lt;/p>
&lt;p>Here are the rules for &lt;code>__iter__&lt;/code> and &lt;code>__next__&lt;/code>.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>__iter__&lt;/code> needs to return an object that has a &lt;code>__next__&lt;/code> method.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>__next__&lt;/code> is a method that returns the next item until there are no more items left. At that point it throws a special exception: the &lt;code>StopIteration&lt;/code> exception.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s start simple by defining &lt;code>__next__&lt;/code> on our &lt;code>WordHunk&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
self.current_index = 0
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
def __next__(self):
if self.current_index &amp;lt; len(self.words):
next_word = self.words[self.current_index]
self.current_index += 1
return next_word
else:
raise StopIteration
&lt;/code>&lt;/pre>
&lt;p>There isn&amp;rsquo;t anything tricky going on here.&lt;/p>
&lt;p>We now have a &lt;code>current_index&lt;/code> variable to keep track of where we are in our word list.&lt;/p>
&lt;p>When we call &lt;code>__next__&lt;/code>, we check if &lt;code>current_index&lt;/code> is a valid index in our word list.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>If &lt;code>current_index&lt;/code> is valid, we return the word at &lt;code>current_index&lt;/code> in our list and increment &lt;code>current_index&lt;/code> by 1.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If &lt;code>current_index&lt;/code> is invalid, we raise a special exception, &lt;code>StopIteration&lt;/code>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>So&lt;code>WordHunk&lt;/code> has a next method. &amp;hellip;What should we do now?&lt;/p>
&lt;p>Let&amp;rsquo;s think back to the requirements for making an object iterable.&lt;/p>
&lt;ul>
&lt;li>The object must have an &lt;code>__iter__&lt;/code> method that returns something with a &lt;code>__next__&lt;/code> method.&lt;/li>
&lt;li>The object&amp;rsquo;s &lt;code>__next__&lt;/code> method must return the next item until it runs out of items, then it throws.&lt;/li>
&lt;/ul>
&lt;p>Hang on&amp;hellip; If &lt;code>WordHunk&lt;/code>&amp;rsquo;s &lt;code>__iter__&lt;/code> method needs to return an object with a &lt;code>__next__&lt;/code> method, and &lt;code>WordHunk&lt;/code> &lt;em>is&lt;/em> an object with a &lt;code>__next__&lt;/code> method&amp;hellip; then that means &lt;code>WordHunk&lt;/code>&amp;rsquo;s &lt;code>__iter__&lt;/code> can just return&amp;hellip; &lt;code>WordHunk&lt;/code>.&lt;/p>
&lt;p>Whoa. That&amp;rsquo;s trippy. But there it is!&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
self.current_index = 0
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
def __next__(self):
if self.current_index &amp;lt; len(self.words):
next_word = self.words[self.current_index]
self.current_index += 1
return next_word
else:
raise StopIteration
def __iter__(self):
self.current_index = 0
return self
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
for word in hunk:
print(word)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>These
are
some
words
&lt;/code>&lt;/pre>
&lt;p>But wait, there&amp;rsquo;s more!&lt;/p>
&lt;p>Defining &lt;em>both&lt;/em> &lt;code>__iter__&lt;/code> and &lt;code>__next__&lt;/code> is a little tiresome, so Python provides some syntactic sugar to help us along.&lt;/p>
&lt;p>We can use the magic of &lt;a href="https://docs.python.org/3/tutorial/classes.html#generators" target="_blank" rel="noopener">generators!&lt;/a>&lt;/p>
&lt;p>Esssentially, generators let us write a &lt;em>single&lt;/em> method that implements the behavior of &lt;em>both&lt;/em> &lt;code>__iter__&lt;/code> and &lt;code>__next__&lt;/code>.&lt;/p>
&lt;p>The syntax is pretty simple. We just need to write a normal method that will return values one at a time (like &lt;code>__next__&lt;/code> did), but instead of using a &lt;code>return&lt;/code> statement, we&amp;rsquo;ll use &lt;code>yield&lt;/code> instead. We can call &lt;em>this&lt;/em> new method &lt;code>__iter__&lt;/code> and completely forget about &lt;code>__next__&lt;/code>.&lt;/p>
&lt;p>&lt;code>yield&lt;/code> is pretty magical. It remembers exactly where it stopped returning values and always picks back up where it left off. It even takes care of throwing a &lt;code>StopIteration&lt;/code> error for us.&lt;/p>
&lt;p>Generators are a little complicated, but an example should clear things up.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
def __iter__(self):
for word in self.words:
yield word
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
for word in hunk:
print(word)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>These
are
some
words
&lt;/code>&lt;/pre>
&lt;p>Now that is a &lt;em>lot&lt;/em> simpler!&lt;/p>
&lt;h1 id="doing-math-with-wordhunks">Doing &amp;ldquo;Math&amp;rdquo; with WordHunks&lt;/h1>
&lt;p>What do you think would happen if we &lt;em>added&lt;/em> two &lt;code>WordHunk&lt;/code>s together using the &lt;code>+&lt;/code> operator?&lt;/p>
&lt;p>I think that addition of two &lt;code>WordHunks&lt;/code> should return a &lt;em>new&lt;/em> &lt;code>WordHunk&lt;/code> with the word lists concatenated together.&lt;/p>
&lt;p>Something like this:&lt;/p>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
another_hunk = WordHunk(&amp;quot;and they are great&amp;quot;)
combined_hunk = hunk + another_hunk
combined_hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
&amp;lt;ipython-input-29-e857aae79163&amp;gt; in &amp;lt;module&amp;gt;()
1 hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
2 another_hunk = WordHunk(&amp;quot;and they are great&amp;quot;)
----&amp;gt; 3 combined_hunk = hunk + another_hunk
4 combined_hunk
TypeError: unsupported operand type(s) for +: 'WordHunk' and 'WordHunk'
&lt;/code>&lt;/pre>
&lt;p>Let&amp;rsquo;s make it happen!&lt;/p>
&lt;p>To get addition to work, we need to define the &lt;code>__add__&lt;/code> method. It takes an argument (the thing that we want to add to our &lt;code>WordHunk&lt;/code>) and returns a new &lt;code>WordHunk&lt;/code> just like we talked about earlier.&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
def __iter__(self):
for word in self.words:
yield word
def __add__(self, other_WordHunk):
all_the_words_list = self.words + other_WordHunk.words
all_the_words_string = &amp;quot; &amp;quot;.join(all_the_words_list)
return WordHunk(all_the_words_string)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">hunk = WordHunk(&amp;quot;These are some words&amp;quot;)
another_hunk = WordHunk(&amp;quot;and they are great&amp;quot;)
combined_hunk = hunk + another_hunk
combined_hunk
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>WordHunk('These are some words and they are great')
&lt;/code>&lt;/pre>
&lt;p>Fantastic!&lt;/p>
&lt;p>Note: Since &lt;code>WordHunk&lt;/code>s are created from strings, we had to convert the new word list into a string.&lt;/p>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>And there you go!&lt;/p>
&lt;p>Thanks to Python&amp;rsquo;s magic methods, our &lt;code>WordHunk&lt;/code> is &lt;em>really&lt;/em> featureful.&lt;/p>
&lt;p>Check it out!&lt;/p>
&lt;pre>&lt;code class="language-python">class WordHunk(object):
def __init__(self, word_string):
self.words = word_string.split()
def __str__(self):
return str(self.words)
def __repr__(self):
return &amp;quot;WordHunk('{}')&amp;quot;.format(&amp;quot; &amp;quot;.join(self.words))
def __len__(self):
return len(self.words)
def __contains__(self, item):
return item in self.words
def __getitem__(self, key):
return self.words[key]
def __setitem__(self, key, new_value):
self.words[key] = new_value
def __iter__(self):
for word in self.words:
yield word
def __add__(self, other_WordHunk):
all_the_words_list = self.words + other_WordHunk.words
all_the_words_string = &amp;quot; &amp;quot;.join(all_the_words_list)
return WordHunk(all_the_words_string)
&lt;/code>&lt;/pre>
&lt;p>This list of magic methods is &lt;em>far&lt;/em> from complete, but it should serve as a good introduction to the kinds of voodoo you have at your disposal.&lt;/p>
&lt;p>If you&amp;rsquo;re hungry for more, I think that &lt;a href="http://www.diveintopython3.net/special-method-names.html" target="_blank" rel="noopener">this guide&lt;/a> is a great place to go next. It &lt;em>also&lt;/em> isn&amp;rsquo;t complete, but it&amp;rsquo;ll give you enough magic methods to solve 98% of your problems.&lt;/p>
&lt;p>That last 2% is &lt;em>really&lt;/em> esoteric (for example, you can manipulate how Python &lt;em>stores&lt;/em> your class&amp;rsquo;s attributes in memory). So I wouldn&amp;rsquo;t worry to much about it. When you need one of those crazy methods, you&amp;rsquo;ll know it.&lt;/p></description></item><item><title>Finally Learn Python Imports</title><link>https://codyvanzandt.com/blog/finally-learn-python-imports/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://codyvanzandt.com/blog/finally-learn-python-imports/</guid><description>&lt;h2 id="what-is-an-import">What Is an Import?&lt;/h2>
&lt;p>&lt;strong>Imports&lt;/strong> are statements that allow us to use code that lives somewhere different from where we&amp;rsquo;re currently working. We cannot (or rather, we absolutely should not) develop even a modest Python program without them. They&amp;rsquo;re essential.&lt;/p>
&lt;p>They&amp;rsquo;re also &lt;em>significantly&lt;/em> more complicated than most folks assume - filled with plenty of traps for unwary programmers to fall into.&lt;/p>
&lt;p>In this notebook, we&amp;rsquo;ll cover imports from the basics (&amp;ldquo;What even IS an import?&amp;quot;) to the edge of what is possible. (Note: we&amp;rsquo;re speaking from a Python3.2+ perspective).&lt;/p>
&lt;p>Dig in, and enjoy!&lt;/p>
&lt;h3 id="where-does-python-code-live">Where Does Python Code Live?&lt;/h3>
&lt;p>Before we can start talking about importing Python code from various places, we need to first discuss the places in which Python code lives.&lt;/p>
&lt;h3 id="modules">Modules&lt;/h3>
&lt;p>Python code lives in files that end in &amp;ldquo;.py&amp;rdquo;.
In Python-speak, a file that ends in &amp;ldquo;.py&amp;rdquo; is also a &lt;strong>module&lt;/strong>.&lt;/p>
&lt;p>For example, let&amp;rsquo;s write some Python code to create a dinner plate and put it in a file named &lt;code>dinner_plate.py&lt;/code>&lt;/p>
&lt;p>&lt;code>dinner_plate.py&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">class DinnerPlate( object ):
def __repr__( self ):
return &amp;quot;DinnerPlate()&amp;quot;
def make_dinner_plate():
return DinnerPlate()
&lt;/code>&lt;/pre>
&lt;p>Here&amp;rsquo;s what our world looks like:&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── dinner_plate.py
└── GuideToImports.ipynb # We're right here!
&lt;/code>&lt;/pre>
&lt;p>We&amp;rsquo;re currently inside of the &lt;code>GuideToImports.ipynb&lt;/code> Jupyter Notebook, and right beside us is a single file named &lt;code>dinner_plate.py&lt;/code>. Since Python files are modules, we could also say that we have a single module named &lt;code>dinner_plate&lt;/code>.&lt;/p>
&lt;p>There&amp;rsquo;s a weird convention here that we need to talk about.&lt;/p>
&lt;p>Notice that when we mentioned the file, we called it &lt;code>dinner_plate.py&lt;/code>. But when we mentioned the module, we called it &lt;code>dinner_plate&lt;/code>. There&amp;rsquo;s only one object here, but we use two different names for it. When we&amp;rsquo;re talking about this object as a file, we call it &lt;code>dinner_plate.py&lt;/code> - &lt;em>with&lt;/em> the &amp;ldquo;.py&amp;rdquo;. But when we&amp;rsquo;re talking about this object as a module, we call it &lt;code>dinner_plate&lt;/code> - &lt;em>without&lt;/em> the &amp;ldquo;.py&amp;rdquo;.&lt;/p>
&lt;p>Yeah, yeah, yeah. It sounds totally stupid to have two &lt;em>slightly different&lt;/em> names for the same thing. But, believe it or not, this is going to end up mattering a LOT.&lt;/p>
&lt;h3 id="packages">Packages&lt;/h3>
&lt;p>If Python code lives in modules, then where do modules live?&lt;/p>
&lt;p>Modules live in folders (also called directories), or in Python-speak, &lt;strong>packages&lt;/strong>.
Let me repeat that: packages are just folders.&lt;/p>
&lt;p>Just as modules allow us to group Python snippets together, packages allow us to group modules together.&lt;/p>
&lt;p>For example, let&amp;rsquo;s say that, in addition to our &lt;code>dinner_plate&lt;/code> module, we create a similar module, &lt;code>cup&lt;/code>, and we put it right beside &lt;code>dinner_plate&lt;/code>.&lt;/p>
&lt;p>&lt;code>cup&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">class Cup( object ):
def __repr__( self ):
return &amp;quot;Cup()&amp;quot;
def make_cup():
return Cup()
&lt;/code>&lt;/pre>
&lt;p>Here&amp;rsquo;s what our world looks like now:&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── cup.py
├── dinner_plate.py
└── GuideToImports.ipynb # We're right here!
&lt;/code>&lt;/pre>
&lt;p>Now this is all well and good, but we might want to group &lt;code>dinner_plate&lt;/code> and &lt;code>cup.&lt;/code> together into a package named &lt;code>flatware&lt;/code>. So, we create a folder named &lt;code>flatware&lt;/code> and move &lt;code>cup&lt;/code> and &lt;code>dinner_plate&lt;/code> into that folder.&lt;/p>
&lt;p>Now we have a &lt;strong>package&lt;/strong> named &lt;code>flatware&lt;/code> that contains two modules named &lt;code>cup&lt;/code> and &lt;code>dinner_plate&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── flatware
│ ├── cup.py
│ └── dinner_plate.py
└── GuideToImports.ipynb # We're right here!
&lt;/code>&lt;/pre>
&lt;p>Lastly, packages can also have more packages inside of them! We call these inner packages &lt;strong>subpackages&lt;/strong>, but there&amp;rsquo;s nothing special about them. They just regular packages that happen to live inside of another package.&lt;/p>
&lt;p>For example, let&amp;rsquo;s create a subpackge inside of &lt;code>flatware&lt;/code> named &lt;code>utensils&lt;/code> and add two modules to it: &lt;code>fork&lt;/code> and &lt;code>spoon&lt;/code>.&lt;/p>
&lt;p>&lt;code>fork.py&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">class Fork( object ):
def __repr__( self ):
return &amp;quot;Fork()&amp;quot;
def make_fork():
return Fork()
&lt;/code>&lt;/pre>
&lt;p>&lt;code>spoon.py&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">class Spoon( object ):
def __repr__( self ):
return &amp;quot;Spoon()&amp;quot;
def make_spoon():
return Spoon()
&lt;/code>&lt;/pre>
&lt;p>Now we have a package named &lt;code>flatware&lt;/code> that contains two modules, &lt;code>cup&lt;/code> and &lt;code>dinner_plate&lt;/code>, as well as a subpacakge named &lt;code>utensilts&lt;/code>. The &lt;code>utensils&lt;/code> subpackage has two modules,&lt;code>fork&lt;/code> and &lt;code>spoon&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── flatware
│ ├── cup.py
│ ├── dinner_plate.py
│ └── utensils
│ ├── fork.py
│ └── spoon.py
└── GuideToImports.ipynb # We're right here!
&lt;/code>&lt;/pre>
&lt;h2 id="imports-101-the-basics-of-using-imports">Imports 101: The Basics of Using Imports&lt;/h2>
&lt;p>Now that we know where Python code lives, it&amp;rsquo;s time to get back to our original question.&lt;/p>
&lt;p>How do we &lt;strong>import&lt;/strong> a Python object from &lt;strong>where it lives&lt;/strong> into &lt;strong>where we want to use it?&lt;/strong>&lt;/p>
&lt;p>To import an object, we need to do two things:&lt;/p>
&lt;ol>
&lt;li>Identify the complete &lt;strong>path&lt;/strong> from the top of our project to the object we want to import&lt;/li>
&lt;li>Write an import statement&lt;/li>
&lt;/ol>
&lt;p>Keeping these two things in mind, let&amp;rsquo;s go import some stuff!&lt;/p>
&lt;h3 id="importing-a-module">Importing a Module&lt;/h3>
&lt;p>Let&amp;rsquo;s try to import the &lt;code>fork&lt;/code> module!&lt;/p>
&lt;h4 id="step-1-identifying-the-complete-path">Step 1: Identifying the Complete Path&lt;/h4>
&lt;p>We need to get the complete path from the top of our project to where &lt;code>fork&lt;/code> is.&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── flatware
│ ├── cup.py
│ ├── dinner_plate.py
│ └── utensils
│ ├── fork.py
│ └── spoon.py
└── GuideToImports.ipynb # We're right here!
&lt;/code>&lt;/pre>
&lt;p>To get from the top of our project to &lt;code>fork&lt;/code>, we have to go into the &lt;code>flatware&lt;/code> package, then into the &lt;code>utensils&lt;/code> package, and finally into the &lt;code>fork&lt;/code> module.&lt;/p>
&lt;p>This means that the complete path from where we are to &lt;code>fork&lt;/code> is:&lt;/p>
&lt;p>&lt;code>flatware.utensils.fork&lt;/code>&lt;/p>
&lt;h4 id="step-2-writing-the-import-statement">Step 2: Writing the Import Statement&lt;/h4>
&lt;p>Now that we have our path, &lt;code>flatware.utensils.fork&lt;/code>, we&amp;rsquo;re ready to write a real Python import statement.&lt;/p>
&lt;p>Here&amp;rsquo;s the syntax:&lt;/p>
&lt;pre>&lt;code class="language-python">import flatware.utensils.fork
&lt;/code>&lt;/pre>
&lt;p>And that we have it! We have successfully imported the &lt;code>fork&lt;/code> module!&lt;/p>
&lt;p>Take note: import statements import &lt;strong>modules&lt;/strong>, not files. This means that we imported the &lt;code>fork&lt;/code> &lt;strong>module&lt;/strong>, not the &lt;code>fork.py&lt;/code> file.&lt;/p>
&lt;p>We should never us &amp;ldquo;.py&amp;rdquo; when we&amp;rsquo;re trying to import a module. &lt;em>Because modules do not end in &amp;ldquo;.py&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>(See! The whole file vs. module thing from earliler came in handy!)&lt;/p>
&lt;h3 id="using-the-imported-module">Using the Imported Module&lt;/h3>
&lt;p>Now that we&amp;rsquo;ve imported &lt;code>fork&lt;/code>, we&amp;rsquo;re now ready to use the two objects inside of &lt;code>fork&lt;/code>: the class &lt;code>Fork&lt;/code> and the function &lt;code>make_fork&lt;/code>.&lt;/p>
&lt;p>To use one of these objects - for example, the function &lt;code>make_fork&lt;/code> - we need to use the complete path to &lt;code>make_fork&lt;/code>: &lt;code>flatware.utensils.fork.make_fork&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">import flatware.utensils.fork
flatware.utensils.fork.make_fork()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>Fork()
&lt;/code>&lt;/pre>
&lt;p>We got a fork object! Hurray!&lt;/p>
&lt;p>That was pretty painless. The only headache was how much we had to type in order to call the &lt;code>make_fork&lt;/code> function: &lt;code>flatware.utensils.fork.make_fork()&lt;/code>&lt;/p>
&lt;p>Luckily, Python provides a handy way around that.&lt;/p>
&lt;h3 id="importing-an-individual-object-from-a-module">Importing an Individual Object from a Module&lt;/h3>
&lt;p>In our previous example, we imported the &lt;code>fork&lt;/code> module and then accessed the &lt;code>make_fork&lt;/code> function inside of &lt;code>fork&lt;/code>.&lt;/p>
&lt;p>This time, we&amp;rsquo;re going to try something different. Instead of importing the &lt;code>fork&lt;/code> module, we&amp;rsquo;re going to import an single object from the &lt;code>fork&lt;/code> module.&lt;/p>
&lt;p>For example, we can reach into the &lt;code>fork&lt;/code> module and import just the &lt;code>make_fork&lt;/code> function:&lt;/p>
&lt;pre>&lt;code class="language-python">from flatware.utensils.fork import make_fork
make_fork()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>Fork()
&lt;/code>&lt;/pre>
&lt;p>And there&amp;rsquo;s our Fork again!&lt;/p>
&lt;p>Notice how we no longer have to call &lt;code>flatware.utensils.fork.make_fork()&lt;/code>, we can simply write &lt;code>make_fork()&lt;/code>. Convenient!&lt;/p>
&lt;p>Take note: we didn&amp;rsquo;t import all of the objects that live in the &lt;code>fork&lt;/code> module; we just imported &lt;code>make_fork&lt;/code>. If we tried to access &lt;em>anything else&lt;/em> from the &lt;code>fork&lt;/code> module, we would run into an error.&lt;/p>
&lt;p>Luckily, this &amp;ldquo;from &amp;hellip; &amp;quot; syntax doesn&amp;rsquo;t limit us to importing only a single object. If we wanted to import the class &lt;code>Fork&lt;/code> &lt;em>and&lt;/em> the function &lt;code>make_fork&lt;/code> from the &lt;code>fork&lt;/code> module, we can do that in a single statement:&lt;/p>
&lt;pre>&lt;code class="language-python">from flatware.utensils.fork import make_fork, Fork
make_fork(), Fork()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>(Fork(), Fork())
&lt;/code>&lt;/pre>
&lt;h3 id="nicknaming-our-imported-objects">Nicknaming Our Imported Objects&lt;/h3>
&lt;p>We&amp;rsquo;ve now seen two different kinds of imports:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>import a.path.to.a.module&lt;/strong>&lt;/li>
&lt;li>and &lt;strong>from a.path.to.a.module import something&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>But wait! There&amp;rsquo;s more!&lt;/p>
&lt;p>We can also give &lt;strong>nicknames&lt;/strong> to the objects that we import!&lt;/p>
&lt;p>Check this out:&lt;/p>
&lt;pre>&lt;code class="language-python">import flatware.utensils.fork as my_fork_module
my_fork_module.make_fork()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>Fork()
&lt;/code>&lt;/pre>
&lt;p>In writing &lt;code>as my_fork_module&lt;/code>, we&amp;rsquo;re giving a nickname to &lt;code>my_fork_module&lt;/code> that only applies here. The original &lt;code>fork&lt;/code> module didn&amp;rsquo;t change.&lt;/p>
&lt;p>Now when we want to call the function &lt;code>make_fork&lt;/code>, we can use our nickname, &lt;code>my_fork_module&lt;/code>.&lt;/p>
&lt;p>Additionally, we can use nicknames with &lt;strong>from&lt;/strong> imports!&lt;/p>
&lt;pre>&lt;code class="language-python">from flatware.utensils.fork import make_fork as fork_function
fork_function()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>Fork()
&lt;/code>&lt;/pre>
&lt;p>We can even import multiple objects &lt;em>and&lt;/em> rename them:&lt;/p>
&lt;pre>&lt;code class="language-python">from flatware.utensils.fork import make_fork as fork_function, Fork as ForkClass
fork_function(), ForkClass()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>(Fork(), Fork())
&lt;/code>&lt;/pre>
&lt;h3 id="absolute-and-relative-imports">Absolute and Relative Imports&lt;/h3>
&lt;p>We haven&amp;rsquo;t mentioned this before, but there are actually two different kinds of imports: absolute imports and relative imports.&lt;/p>
&lt;h4 id="absolute-imports">Absolute Imports&lt;/h4>
&lt;p>With absolute imports, we have to include the name of every package and module in the path from the top of our project to the object we want to import.&lt;/p>
&lt;p>absolute imports are what we&amp;rsquo;ve been using this whole time.&lt;/p>
&lt;h4 id="relative-imports">Relative Imports&lt;/h4>
&lt;p>Relative imports are different.&lt;/p>
&lt;p>With relative imports, we &lt;strong>don&amp;rsquo;t&lt;/strong> have to include the name of every pacakge in our path. Instead, we can use special nicknames that Python assigns to packages depending on their position &lt;strong>relative&lt;/strong> to where we are.&lt;/p>
&lt;p>Specifically:&lt;/p>
&lt;ol>
&lt;li>the package we&amp;rsquo;re currently inside is nicknamed: &lt;strong>.&lt;/strong> (a single dot)&lt;/li>
&lt;li>the package one level above us is nicknamed: &lt;strong>..&lt;/strong> (two dots)&lt;/li>
&lt;li>the package two levels above us is nicknamed: &lt;strong>&amp;hellip;&lt;/strong> (three dots)&lt;/li>
&lt;/ol>
&lt;p>&amp;hellip; and so on&amp;hellip;&lt;/p>
&lt;p>As an example, let&amp;rsquo;s say we&amp;rsquo;re working inside of &lt;code>fork.py&lt;/code>, and we want to import the &lt;code>make_cup&lt;/code> function from &lt;code>cup.py&lt;/code> and the &lt;code>make_spoon&lt;/code> function from &lt;code>spoon.py&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-text">.
├── flatware
│ ├── cup.py
│ ├── dinner_plate.py
│ └── utensils
│ ├── fork.py # Now we're right here!
│ └── spoon.py
└── GuideToImports.ipynb
&lt;/code>&lt;/pre>
&lt;p>If we were using absolute imports, we would the full path from the beginning of our project to the objects that we want to import:&lt;/p>
&lt;pre>&lt;code class="language-python">from flatware.utensils.spoon import make_spoon
from flatware.cup import make_cup
&lt;/code>&lt;/pre>
&lt;p>But with relative imports, we can replace package names with dots.&lt;/p>
&lt;p>Since we&amp;rsquo;re currently in &lt;code>fork.py&lt;/code>&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>&lt;strong>.&lt;/strong> (single dot) refers to our current package: &lt;code>flatware.utensils&lt;/code>&lt;/li>
&lt;li>&lt;strong>&amp;rdquo;..&amp;quot;&lt;/strong> (double dot) refers to the pacakge above us: &lt;code>flateware&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Making those replacements yields this:&lt;/p>
&lt;pre>&lt;code class="language-python">from .spoon import make_spoon
from ..cup import make_cup
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>Important Note:&lt;/strong> If we want to use relative imports, we must use &lt;code>from&lt;/code>.&lt;/p>
&lt;h4 id="absolute-and-relative-which-should-we-use">Absolute and Relative: Which Should We Use?&lt;/h4>
&lt;p>Generally speaking, we should prefer absolute imports over relative imports.&lt;/p>
&lt;p>Absolute imports are clearer than their relative counterparts. With absolute imports, we have the full and complete paths to our imported objects. With relative imports, we have to mentally convert the dots into packages and hope that we got it right.&lt;/p>
&lt;p>Of course, there is one big advantages to relative imports. Relative imports don&amp;rsquo;t depend on the exact names of our packages. If we change the names of our packages but do not change their structure, absolute imports will break, but relative imports will still succeed.&lt;/p>
&lt;p>Is the extra flexibility of relative imports worth sacrificing the clarity of absolute imports? Perhaps not. These days, most text-editors and IDEs are capable of automatically renaming things for us &amp;ndash; we don&amp;rsquo;t &lt;em>need&lt;/em> the flexibility of relative imports.&lt;/p>
&lt;p>Of course this is a decision everyone has to make on their own.&lt;/p>
&lt;h2 id="imports-102-importing-a-module-vs-running-a-module-vs-running-a-file">Imports 102: Importing a Module vs. Running a Module vs. Running a File&lt;/h2>
&lt;p>Now that we have explored the basics, it&amp;rsquo;s time to venture off the garden path. Here&amp;rsquo;s perhaps the most horrifying element of Python imports&amp;hellip;&lt;/p>
&lt;p>&lt;strong>We can have perfectly good imports that break completely depending on how we use our modules.&lt;/strong>&lt;/p>
&lt;p>For example, if we have a file, &lt;code>cool_code.py&lt;/code>, that imports some things, those imports will behave differently depending on if we&amp;hellip;&lt;/p>
&lt;ol>
&lt;li>Import the &lt;code>cool_code&lt;/code> module&lt;/li>
&lt;li>Run the &lt;code>cool_code&lt;/code> module&lt;/li>
&lt;li>Run the &lt;code>cool_code.py&lt;/code> file&lt;/li>
&lt;/ol>
&lt;p>(Also! Notice how, once again, we&amp;rsquo;re differentiating between the &lt;code>cool_code&lt;/code> module and the &lt;code>cool_code.py&lt;/code> file.)&lt;/p>
&lt;p>This sounds&amp;hellip; nasty, confusing, and disappointing.&lt;/p>
&lt;p>Let&amp;rsquo;s try and make sense of this using an example. Here&amp;rsquo;s a simple project:&lt;/p>
&lt;pre>&lt;code class="language-text">vehicles
├── engines
│ └── truck_engine.py
└── trucks
└── chevy.py
&lt;/code>&lt;/pre>
&lt;p>&lt;code>truck_engine&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">class TruckEngine( object ):
def start( self ):
return &amp;quot;VROOM!&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>&lt;code>chevy&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">from vehicles.engines.truck_engine import TruckEngine
class Silverado( object ):
def __init__( self ):
self.engine = TruckEngine()
def start( self ):
return self.engine.start()
&lt;/code>&lt;/pre>
&lt;p>We have a &lt;code>vehicle&lt;/code> package with two subpackages: &lt;code>engines&lt;/code> and &lt;code>trucks&lt;/code>. The &lt;code>engines&lt;/code> subpackage has a &lt;code>truck_engine&lt;/code> module with a &lt;code>TruckEngine&lt;/code> class. The &lt;code>trucks&lt;/code> subpackage has a &lt;code>chevy&lt;/code> module with a &lt;code>Silverado&lt;/code> class.&lt;/p>
&lt;p>The &lt;code>Silverado&lt;/code> class needs an engine, so it imports &lt;code>Truck&lt;/code> engine from the top of the project using the path &lt;code>vehicles.engines.truck_engine&lt;/code>.&lt;/p>
&lt;h3 id="importing-a-module-1">Importing a Module&lt;/h3>
&lt;p>Let&amp;rsquo;s try importing the &lt;code>Silverado&lt;/code> class and using it!&lt;/p>
&lt;pre>&lt;code class="language-python">from vehicles.trucks.chevy import Silverado
myTruck = Silverado()
myTruck.start()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>'VROOM!'
&lt;/code>&lt;/pre>
&lt;p>We imported our &lt;code>Silverado&lt;/code> class from the &lt;code>chevy&lt;/code> module and used it; everything worked perfectly.&lt;/p>
&lt;p>No surprises here!&lt;/p>
&lt;h3 id="running-a-python-file">Running a Python File&lt;/h3>
&lt;p>Now let&amp;rsquo;s try running our &lt;code>chevy.py&lt;/code> file. Our imports are good, and our Python is solid. Everything &lt;em>should&lt;/em> work fine. Let&amp;rsquo;s give it a go.&lt;/p>
&lt;p>To run a Python file, we need to open up our command line (also called the terminal) and enter the &lt;code>python&lt;/code> command followed by the path through our filesystem to our &lt;code>chevy.py&lt;/code> file.&lt;/p>
&lt;p>Like this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-bash">%%bash
python vehicles/trucks/chevy.py
&lt;/code>&lt;/pre>
&lt;pre>&lt;code>Traceback (most recent call last):
File &amp;quot;vehicles/trucks/chevy.py&amp;quot;, line 1, in &amp;lt;module&amp;gt;
from vehicles.engines.truck_engine import TruckEngine
ModuleNotFoundError: No module named 'vehicles'
---------------------------------------------------------------------------
CalledProcessError Traceback (most recent call last)
&amp;lt;ipython-input-24-7f6debbca43d&amp;gt; in &amp;lt;module&amp;gt;
----&amp;gt; 1 get_ipython().run_cell_magic('bash', '', 'python vehicles/trucks/chevy.py\n')
~/.local/share/virtualenvs/GuideToImports-e6Nz-2ie/lib/python3.7/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
2350 with self.builtin_trap:
2351 args = (magic_arg_s, cell)
-&amp;gt; 2352 result = fn(*args, **kwargs)
2353 return result
2354
~/.local/share/virtualenvs/GuideToImports-e6Nz-2ie/lib/python3.7/site-packages/IPython/core/magics/script.py in named_script_magic(line, cell)
140 else:
141 line = script
--&amp;gt; 142 return self.shebang(line, cell)
143
144 # write a basic docstring:
&amp;lt;/home/cody/.local/share/virtualenvs/GuideToImports-e6Nz-2ie/lib/python3.7/site-packages/decorator.py:decorator-gen-110&amp;gt; in shebang(self, line, cell)
~/.local/share/virtualenvs/GuideToImports-e6Nz-2ie/lib/python3.7/site-packages/IPython/core/magic.py in &amp;lt;lambda&amp;gt;(f, *a, **k)
185 # but it's overkill for just that one bit of state.
186 def magic_deco(arg):
--&amp;gt; 187 call = lambda f, *a, **k: f(*a, **k)
188
189 if callable(arg):
~/.local/share/virtualenvs/GuideToImports-e6Nz-2ie/lib/python3.7/site-packages/IPython/core/magics/script.py in shebang(self, line, cell)
243 sys.stderr.flush()
244 if args.raise_error and p.returncode!=0:
--&amp;gt; 245 raise CalledProcessError(p.returncode, cell, output=out, stderr=err)
246
247 def _run_script(self, p, cell, to_close):
CalledProcessError: Command 'b'python vehicles/trucks/chevy.py\n'' returned non-zero exit status 1.
&lt;/code>&lt;/pre>
&lt;p>Oh, the horror!&lt;/p>
&lt;p>What went wrong? Let&amp;rsquo;s check out the error message at the &lt;em>very&lt;/em> top and see if we can make any sense of this mess&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-text">Traceback (most recent call last):
File &amp;quot;vehicles/trucks/chevy.py&amp;quot;, line 1, in &amp;lt;module&amp;gt;
from vehicles.engines.truck_engine import TruckEngine
ModuleNotFoundError: No module named 'vehicles'
&lt;/code>&lt;/pre>
&lt;p>When our &lt;code>chevy&lt;/code> module tried to import &lt;code>TruckEngine&lt;/code>, it failed because it couldn&amp;rsquo;t find a module named &lt;code>vehicles&lt;/code>.&lt;/p>
&lt;p>This is confusing for at least two reasons:&lt;/p>
&lt;ol>
&lt;li>This import worked just fine a minute ago&lt;/li>
&lt;li>What does Python mean by &lt;strong>No module named &amp;lsquo;vehicle&amp;rsquo;?&lt;/strong> Vehicle is a &lt;strong>package!&lt;/strong> And it&amp;rsquo;s right &lt;strong>there!&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>To understand what&amp;rsquo;s going on, we need to understand how Python actually finds modules to import.&lt;/p>
&lt;h4 id="how-python-finds-modules">How Python Finds Modules&lt;/h4>
&lt;p>When Python starts up, it creates a giant list of places where it&amp;rsquo;ll look for modules that we want to import. If our module isn&amp;rsquo;t in one of those places on the list, Python won&amp;rsquo;t find it and all sorts of nasty errors will pop up.&lt;/p>
&lt;p>This giant list of places is actually just a variable named &lt;code>path&lt;/code> that lives inside of Python&amp;rsquo;s built-in &lt;code>sys&lt;/code> module. It&amp;rsquo;s just a regular Python list with regular Python strings inside of it. We can import it and muck around with it just like any other list.&lt;/p>
&lt;p>Weirdly enough, the list &lt;code>sys.path&lt;/code> starts out &lt;em>completely empty&lt;/em>. There&amp;rsquo;s nothing there, and if it stayed that way, we wouldn&amp;rsquo;t be able to import anything. Luckily, Python automatically adds some things to &lt;code>sys.path&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>Python adds the directory containing all of Python&amp;rsquo;s built-in modules&lt;/li>
&lt;li>Python adds the directory containing all of the extra packages we&amp;rsquo;ve personally installed&lt;/li>
&lt;li>And if we run a Python file (like we did just a minute ago), Python adds the directory containing the file we ran.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Yeah, yeah, yeah. This is super interesting or whatever, BUT WHAT DOES ANY OF THIS MEAN???&lt;/strong>&lt;/p>
&lt;p>Our error noted that Python couldn&amp;rsquo;t find the &lt;code>vehicles&lt;/code> package, so &lt;code>vehicles&lt;/code> must not have been in &lt;code>sys.path&lt;/code>!&lt;/p>
&lt;p>So let&amp;rsquo;s think about what &lt;strong>was&lt;/strong> in &lt;code>sys.path&lt;/code>. As per above, &lt;code>sys.path&lt;/code> has the directory containing all of the built-in modules, the directory containing all of our installed modules, and finally, &lt;code>sys.path&lt;/code> has the directory containing the file we ran.&lt;/p>
&lt;p>Since we ran &lt;code>python vehicles/trucks/chevy.py&lt;/code>, the directory containing &lt;code>chevy.py&lt;/code> was &lt;code>trucks/&lt;/code>. So, the &lt;code>trucks/&lt;/code> direcgtory got added in &lt;code>sys.path&lt;/code>.&lt;/p>
&lt;p>See the problem? The &lt;code>vehicles/&lt;/code> directory never got added to &lt;code>sys.path&lt;/code>! This means that our import statement inside of the &lt;code>chevy&lt;/code> module&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">from vehicles.engines.truck_engine import TruckEngine
&lt;/code>&lt;/pre>
&lt;p>&amp;hellip;completely fails because Python cannot find the &lt;code>vehicles/&lt;/code> part of our path!&lt;/p>
&lt;h4 id="so-can-we-fix-this-or-can-we-never-run-python-code-again">So&amp;hellip; Can We Fix This? Or Can We Never Run Python Code Again?&lt;/h4>
&lt;p>Well, there&amp;rsquo;s the naive solution: right before Python reaches our &lt;code>from vehicles.engines.truck_engine ...&lt;/code> import statement, we could manually add the &lt;code>vehicles/&lt;/code> directory to &lt;code>sys.path&lt;/code>. After all, &lt;code>sys.path&lt;/code> is just a list. We can add things to it.&lt;/p>
&lt;p>This would work&amp;hellip; but it is a &lt;strong>horrible&lt;/strong> solution. It it just so hacky, so adhoc, and so ugly. It is also annoying: we don&amp;rsquo;t want to add this&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-python">import sys.path
sys.path.append( &amp;quot;some/path/to/some/place&amp;quot; )`
&lt;/code>&lt;/pre>
&lt;p>&amp;hellip;to the top of every file that might have a problematic import. That&amp;rsquo;s terrible.&lt;/p>
&lt;h4 id="luckily-there-is-another-way">Luckily, There Is Another Way!&lt;/h4>
&lt;p>Instead of running the &lt;strong>file&lt;/strong> &lt;code>vehicles/trucks/chevy.py&lt;/code>, we can instead run the &lt;strong>module&lt;/strong> &lt;code>vehicles.trucks.chevy&lt;/code>&lt;/p>
&lt;p>If we run the &lt;strong>module&lt;/strong> &lt;code>chevy&lt;/code>, Python does something slightly different to &lt;code>sys.path&lt;/code> than when we ran the &lt;strong>file&lt;/strong> &lt;code>chevy.py&lt;/code>. Instead of adding the directory containing the &lt;strong>file&lt;/strong> (which was &lt;code>/trucks&lt;/code>), Python will add our current directory to &lt;code>sys.path&lt;/code>.&lt;/p>
&lt;p>Let&amp;rsquo;s remind ourselves what our current directory is&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-text">. # &amp;lt;--- This is our current directory
├── GuideToImports.ipynb # We're right here!
├── flatware
│ ├── cup.py
│ ├── dinner_plate.py
│ └── utensils
│ ├── fork.py
│ └── spoon.py
└── vehicles
├── engines
│ └── truck_engine.py
└── trucks
└── chevy.py
&lt;/code>&lt;/pre>
&lt;p>Perfect! Our current directory contains &lt;code>vehicles/&lt;/code>&lt;/p>
&lt;p>This means that, as long as we run the &lt;code>chevy&lt;/code> &lt;strong>module&lt;/strong> instead of the &lt;code>chevy.py&lt;/code> &lt;strong>file&lt;/strong>, the &lt;code>vehicles&lt;/code> package will be added to &lt;code>sys.path&lt;/code>, thus allowing Python to find &lt;code>vehicles&lt;/code> and everything underneath it!&lt;/p>
&lt;h4 id="but-how-do-we-run-something-as-a-module">But How Do We Run Something as a Module?&lt;/h4>
&lt;p>As it turns out, it&amp;rsquo;s trivially easy to run something as a module instead of as a file.&lt;/p>
&lt;p>Instead of running&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-text">%%bash
python vehicles/trucks/chevy.py
&lt;/code>&lt;/pre>
&lt;p>&amp;hellip;in our command line / terminal, we need to run&amp;hellip;&lt;/p>
&lt;pre>&lt;code class="language-bash">%%bash
python -m vehicles.trucks.chevy
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>AND IT WORKS! WE DON&amp;rsquo;T GET AN ERROR!&lt;/strong>&lt;/p>
&lt;p>There are two things to notice:&lt;/p>
&lt;ol>
&lt;li>We added &amp;ldquo;-m&amp;rdquo; to our command. This tells Python that we&amp;rsquo;re running a &lt;strong>module&lt;/strong> and not a &lt;strong>file&lt;/strong>.&lt;/li>
&lt;li>We used &amp;ldquo;vehicles.trucks.chevy&amp;rdquo;, not &amp;ldquo;vehicles/trucks/chevy.py&amp;rdquo;. This is because&amp;hellip;
&lt;ul>
&lt;li>Files are separated by slashes, but packages and modules (like all of our imports) are separated by dots.&lt;/li>
&lt;li>Pyton files end in &amp;ldquo;.py&amp;rdquo;, but modules do not&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Once again, the seemingly trivial difference between files and modules saves the day once again.&lt;/p>
&lt;h3 id="this-has-been-a-long-difficult-section-lets-summarize">This Has Been a Long, Difficult Section. Let&amp;rsquo;s Summarize.&lt;/h3>
&lt;ul>
&lt;li>If we want to &lt;strong>import&lt;/strong> modules, there&amp;rsquo;s no need to worry. As long as we wrote valid import statements, Python can figure everything out for us.&lt;/li>
&lt;li>But if we want to &lt;strong>run&lt;/strong> a file or a module, we need to think about &lt;code>sys.path&lt;/code>
&lt;ul>
&lt;li>No matter what, &lt;code>sys.path&lt;/code> will contain the directories where built-ins and user-installed modules live.&lt;/li>
&lt;li>If we run a Python &lt;strong>file&lt;/strong>, &lt;code>sys.path&lt;/code> will contain the directory where our file lives&lt;/li>
&lt;li>If we run a Python &lt;strong>module&lt;/strong>, &lt;code>sys.path&lt;/code> will contain our current directory.
&lt;ul>
&lt;li>You run a Python file like this: &lt;code>python path/to/your/file.py&lt;/code>&lt;/li>
&lt;li>And you run a Python module like this: &lt;code>python path.to.your.file&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="imports-201-best-practices">Imports 201: Best Practices&lt;/h2>
&lt;p>Now that we understand how to use imports, it&amp;rsquo;s time to understand how to use imports &lt;em>properly&lt;/em>.&lt;/p>
&lt;h3 id="where-do-we-put-our-imports">Where Do We Put Our Imports?&lt;/h3>
&lt;ul>
&lt;li>We put imports at the very top of our Python modules, right after any comments/docstrings, and right before any module-level variables.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">&amp;quot;&amp;quot;&amp;quot;
Here are some comments! Comment, comment, comment!
&amp;quot;&amp;quot;&amp;quot;
import math
import sys
MODULE_LEVEL_VARIABLE = ( &amp;quot;Big&amp;quot;, &amp;quot;Old&amp;quot;, &amp;quot;Variable&amp;quot; )
&lt;/code>&lt;/pre>
&lt;p>Every other option is terrible. Full stop. Do not suprise folks with hidden imports.&lt;/p>
&lt;h3 id="how-do-we-organize-our-imports">How Do We Organize Our Imports?&lt;/h3>
&lt;ul>
&lt;li>Every new import goes on a new line. The only exception are &lt;code>from...&lt;/code> imports.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">import math
import sys
from my_module import function_one, function_two
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>Group imports from the same packages together&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">import math
import my_package.my_module # Imports from `my_package` are together
import my_package.my_other_module
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>You should have three different sections of imports, each separated by a new line. Those sections are&amp;hellip;
&lt;ul>
&lt;li>Built-in imports&lt;/li>
&lt;li>Third-party imports&lt;/li>
&lt;li>Local imports&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">import math #
import os # math and os are built-in
import pandas
import requests # pandas and requests are third-party modules
from ..my_package.my_module import my_function
import my_pacakge.my_subpackage.my_other_module # These are local imports
&lt;/code>&lt;/pre>
&lt;h3 id="how-do-we-feel-about-relative-vs-absolute-imports-again">How Do We Feel About Relative vs. Absolute Imports Again?&lt;/h3>
&lt;ul>
&lt;li>We generally prefer absolute imports over relative imports.
&lt;ul>
&lt;li>Absolute imports make it clearer where the import is coming from.
&lt;ul>
&lt;li>But if your absolute imports are getting incredibly long, relative imports can help.&lt;/li>
&lt;li>Additionally, if you think that the names of your packages will change (but their structure won&amp;rsquo;t), relative imports can also help.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">from my_package.my_subpackage.my_module import my_function # We tend to prefer this...
from ..my_subpackage.my_module import my_function # ...over this.
&lt;/code>&lt;/pre>
&lt;h3 id="how-do-we-feel-about-nicknames">How Do We Feel About Nicknames?&lt;/h3>
&lt;ul>
&lt;li>We treat them with suspicion, especially if we&amp;rsquo;re nicknaming individual functions, classes, and variables.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">import my_package.my_subpackage.my_subsubpackage.my_module as my_module # This is...fine.
import my_module as jelly_beans # This is seriously questionable
from my_module import function1 as function2 # This is a horrible idea
&lt;/code>&lt;/pre>
&lt;h3 id="how-do-we-feel-about-using-from-to-import-subpackages">How Do We Feel About Using &amp;ldquo;from&amp;hellip;&amp;rdquo; to Import Subpackages?&lt;/h3>
&lt;ul>
&lt;li>We feel okay about importing subpackages, as long as you make sure that your subpackage doesn&amp;rsquo;t share the same name as a third-party or built-in object.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">from my_package import my_subpackage # We feel okay about this...
#...if my_subpackage is unique.
&lt;/code>&lt;/pre>
&lt;h3 id="can-we-import-something-from-somewhere-other-than-where-it-was-defined">Can We Import Something From Somewhere Other Than Where It Was Defined?&lt;/h3>
&lt;ul>
&lt;li>Ugh&amp;hellip; Yeah.. We can do this. &lt;strong>BUT We SHOULDN&amp;rsquo;T!&lt;/strong> We should import objects from the module in which they were originally defined.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>module_a&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">from my_package.my_subpackage import my_function
&lt;/code>&lt;/pre>
&lt;p>&lt;code>module_b&lt;/code>&lt;/p>
&lt;pre>&lt;code class="language-python">from module_a import my_function # BOO! HISS! TERRIBLE!
&lt;/code>&lt;/pre>
&lt;h2 id="where-to-go-from-here">Where to Go from Here?&lt;/h2>
&lt;p>Onwards into &lt;a href="https://docs.python.org/3/reference/import.html#replacing-the-standard-import-system" target="_blank" rel="noopener">the Python documentation!&lt;/a>&lt;/p></description></item></channel></rss>