tag:blogger.com,1999:blog-50188612423555233552025-06-28T05:08:45.530-07:00Write Better PythonAnonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-5018861242355523355.post-60574959689208011012018-10-07T06:29:00.000-07:002018-10-07T06:30:46.538-07:00Python Classes: inherit, override, extend<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.6.8" />
<title>Python Classes: inherit, override, extend</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
</style>
<script type="text/javascript">
/*<+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install();
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>Python Classes: inherit, override, extend</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>Here is a question that comes up from time to time when people start
writing classes in Python: “I defined a derived class but it is not
getting the data from the base class. What’s going on?”</p></div>
<div class="paragraph"><p>Here is a bit of example code that shows this problem:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">A</span></span><span style="color: #990000">(</span>object<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>dataA <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class A'</span>
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">(</span>A<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>dataB <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class B'</span>
b <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"b.dataA:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>dataA<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>But this apparently trivial code fails:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>Traceback (most recent call last):
File "inherit.py", line 34, in <module>
print("b.adata:", b.adata)
AttributeError: 'B' object has no attribute 'adata'</code></pre>
</div></div>
<div class="paragraph"><p>Wait, what? I thought the derived class inherited from the base class,
yet the variable <code>adata</code> set in the base class does not exist? The short
answer is that is how classes work in Python. But the details end up
needing a bit of explanation.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_viewing_class_inheritance_details">Viewing Class Inheritance Details</h2>
<div class="sectionbody">
<div class="paragraph"><p>Before getting to the explanation, it is worth pointing out that you do
not have to take an expert’s word for these things, it is pretty easy
to just stick some diagnostic output into your script to examine what is
happening. In this post I am going to be lazy and just use <code>print()</code>,
but it is worth noting it is usually better to use Python’s logging
module which gives you far more control of the output and where it
goes. But logging is its own topic.</p></div>
<div class="paragraph"><p>So here we will insert some diagnostics into the simple
program:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">A</span></span><span style="color: #990000">(</span>object<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Setting class variables in A'</span><span style="color: #990000">)</span>
clsdataA <span style="color: #990000">=</span> <span style="color: #FF0000">'class A'</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Initializing instance of'</span><span style="color: #990000">,</span> self<span style="color: #990000">.</span>__class__<span style="color: #990000">.</span>__name__<span style="color: #990000">)</span>
self<span style="color: #990000">.</span>dataA <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class A'</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">methA</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">return</span></span> <span style="color: #FF0000">"A method from class A"</span>
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">(</span>A<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Setting class variables in B'</span><span style="color: #990000">)</span>
clsdataB <span style="color: #990000">=</span> <span style="color: #FF0000">'class B'</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Initializing instance of'</span><span style="color: #990000">,</span> self<span style="color: #990000">.</span>__class__<span style="color: #990000">.</span>__name__<span style="color: #990000">)</span>
self<span style="color: #990000">.</span>dataB <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class B'</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Begin examination..."</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Data from classes:"</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"A.clsdataA:"</span><span style="color: #990000">,</span> A<span style="color: #990000">.</span>clsdataA<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"B.clsdataA:"</span><span style="color: #990000">,</span> B<span style="color: #990000">.</span>clsdataA<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Instantiating A as a:"</span><span style="color: #990000">)</span>
a <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">A</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Data from instance a:"</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"a.clsdataA:"</span><span style="color: #990000">,</span> a<span style="color: #990000">.</span>clsdataA<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"a.dataA:"</span><span style="color: #990000">,</span> a<span style="color: #990000">.</span>dataA<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"call methA directly from a:"</span><span style="color: #990000">,</span> a<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">methA</span></span><span style="color: #990000">())</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Instantiating B as b:"</span><span style="color: #990000">)</span>
b <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Data from instance b:"</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"b.clsdataB:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>clsdataB<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"b.dataB:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>dataB<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"b.clsdataA:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>clsdataA<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"call methA from b:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">methA</span></span><span style="color: #990000">())</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"b.dataA:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>dataA<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>When running this, we see:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>Setting class variables in A <b><1></b>
Setting class variables in B
Begin examination...
Data from classes:
A.clsdataA: class A <b><2></b>
B.clsdataA: class A
Instantiating A as a:
Initializing instance of A <b><3></b>
Data from instance a:
a.clsdataA: class A <b><4></b>
a.dataA: instance of class A
call methA directly from a: A method from class A
Instantiating B as b:
Initializing instance of B <b><5></b>
Data from instance b:
b.clsdataB: class B
b.dataB: instance of class B
b.clsdataA: class A <b><6></b>
call methA from b: A method from class A
Traceback (most recent call last):
File "./override.py", line 41, in <module>
print("b.dataA:", b.dataA)
AttributeError: 'B' object has no attribute 'dataA'</code></pre>
</div></div>
<div class="colist arabic"><ol>
<li>
<p>
The two assignments of class data happen
before anything: a class definition is an
executable statement, executed when reached in the file.
</p>
</li>
<li>
<p>
You can access the class data before any
instances are created, as per the previous note.
</p>
</li>
<li>
<p>
The class A initializer is called when A is instantiated.
</p>
</li>
<li>
<p>
Data from an instance of the base class is as expected.
</p>
</li>
<li>
<p>
The class B initializer (only) is called when B is instantiated.
</p>
</li>
<li>
<p>
The class data from the base class and the method from
the base class are inherited as expected.
</p>
</li>
</ol></div>
<div class="paragraph"><p>So now we have a good idea what happened. Instantiating the derived class
never called the <code>__init__</code> method in the base class. That is
because inheritance is child-oriented in Python. If the base class
defines something and the derived class does not, the reference resolves
to the one in the base class. But here we have defined an <code>__init__</code>
in the derived/child class, so the reference resolves to that. Think of
a given class instance as a dictionary, where there’s one (and only one)
mapping of each name in <code>self</code> to the object that will correspond to
that name.</p></div>
<div class="paragraph"><p>Python has a term for the way things are looked up, the <em>Method Resolution
Order</em> (MRO). The MRO documentation is quite detailed because it deals
also with complex multiple inheritance questions (something many OO
languages just avoid by not allowing multiple inheritance at all), but
the situation here is pretty simple: just walk up the tree, until you
find a match; if a method is not found in any of the explicit defintions,
it will pick the method from the <code>object</code> class, which is the base of
all so-called New-Style Classes and so is there implicitly even if not
listed. This "walk up" concept is very important and we’ll come back
to it.</p></div>
<div class="paragraph"><p>So after a lot of words, we can see how to "fix" our initial
problem: make a call to the base class initializer if we think
we need that initialization to happen:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">A</span></span><span style="color: #990000">(</span>object<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>dataA <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class A'</span>
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">(</span>A<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
A<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">)</span>
self<span style="color: #990000">.</span>dataB <span style="color: #990000">=</span> <span style="color: #FF0000">'instance of class B'</span>
b <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">B</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span>b<span style="color: #990000">.</span>dataA<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>After this simple addition, the output is as we might originally
have expected, instead of the previous error:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>instance of class A</code></pre>
</div></div>
<div class="paragraph"><p>The order matters, it might be worth trying the experiment again
with the initializer call and the assignment to <code>self.dataB</code>
swapped to see this. It means we can perform steps both before
and after the call to the parent’s method, if we need to.</p></div>
<div class="paragraph"><p>There is a subtlety here: because we are calling class <code>A</code> by name,
rather than through an instance, the the <code>__init__</code> method of <code>A</code>
does not get automatically supplied with an instance reference and you
would get an error if you did not supply it (specifically,
<code>TypeError: __init__() missing 1 required positional argument: 'self'</code>).</p></div>
<div class="paragraph"><p>This behavior is not limited to the <code>__init__</code> function, any method
of the base class can be called, which means the derived class has the
flexibility to tailor the behavior it wants: inherit from the base
clase without doing anything, override the base class, or "extend"
the base class by doing some local work before or after calling the
base class method. You can even extend methods from builtin classes -
the facility is by no means limited to your own classes.</p></div>
<div class="sidebarblock">
<div class="content">
<div class="title">The Method Resolution Order</div>
<div class="paragraph"><p>If you are interested in the MRO, it can actually just be printed out. For
the code above, add this line:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span>B<span style="color: #990000">.</span>__mro__<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>Which would give this response:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>(<class '__main__.B'>, <class '__main__.A'>, <type 'object'>)</code></pre>
</div></div>
</div></div>
<div class="paragraph"><p>As noted earlier, this was a simple case with no surprises, so the
order is simple.</p></div>
<div class="sidebarblock">
<div class="content">
<div class="title">Introspection</div>
<div class="paragraph"><p>Python makes it easy to look inside objects to see what
they look like. For example, to see data defined in an instance,
we can print out the <code>__dict__</code> attribute of the instance.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"Dict:"</span><span style="color: #990000">,</span> b<span style="color: #990000">.</span>__dict__<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>Before adding the the call to the base class initializer the
dictionary would look like:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>Dict: {'dataB': 'instance of class B'}</code></pre>
</div></div>
<div class="paragraph"><p>Afterwards:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>Dict: {'dataA': 'instance of class A', 'dataB': 'instance of class B'}</code></pre>
</div></div>
<div class="paragraph"><p>This actually shows more clearly than the previous words the difference
between having called up to the base class initialization method and not:
in the second case, base’s <code>__init__</code> method has added an entry to
the data dictionary of the child instance.</p></div>
<div class="paragraph"><p>You can see all the defined symbols (methods and data) in the scope of
the instance object by adding this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">print</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"dir:"</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #000000">dir</span></span><span style="color: #990000">(</span>b<span style="color: #990000">))</span></tt></pre></div></div>
</div></div>
</div>
</div>
<div class="sect1">
<h2 id="_the_code_super_code_method">The <code>super</code> Method</h2>
<div class="sectionbody">
<div class="paragraph"><p>All of the above is pretty standard stuff about how Python classes
work. There can always be surprises when you come from a familiar
language to a new one and things look kind of similar but something is
subtly different, but that is just part of learning.</p></div>
<div class="paragraph"><p>Calling to a base class by name, however, may or may not be a good
idea. It is very clear what you mean, but it is not very flexible.
You hardcode a name; if you later change the definition of the derived
class to inherit from some other class, you have to update any calls
to the previous base class to update the name. In fact, for Python 3,
a class which does not state a base class still has one: <code>object</code>;
it is not terribly clear to a reader if you call a method by name in
a base class but the base class does not visibly define it. Once you
start playing with multiple inheritance then you have to do some work
to figure out where the call is going - see the MRO sidebar. This is a
potential code maintainability issue.</p></div>
<div class="paragraph"><p>There is a tool to make that happen cleanly, the built in <code>super</code>
function. In Python 3 <code>super</code> is easy to use in simple cases. For our
modified short example, replace the explicit call to the initializer
for class A with this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #000000">super</span></span><span style="color: #990000">().</span><span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">()</span></tt></pre></div></div>
<div class="paragraph"><p><code>super</code> uses data from the context of the call to figure out where to
delegate it to: the class where the call appears, and the ancestry
tree of the instance. So we do not need to supply the <code>self</code> argument
in this case, unlike the hardwired call to a class’s init method.</p></div>
<div class="paragraph"><p><code>super</code> in Python2 is a little dfferent, look it up if you need to use
it there.</p></div>
<div class="paragraph"><p>The "walk up from the bottom" nature of <code>super</code> means you have to give
some thought when designing your classes. From the viewpoint of a given
method, calling <code>super</code> does not mean call my immediate parent’s method,
it means walk up from <code>self</code> until you find it. Remembering that <code>self</code>
written in a method does not mean "me", it means the instance object I
was called with, which could very well be an instance of a class that
derived from me. So be nice to others who may want to derive from you,
don’t build in assumptions that may not work for derived classes.</p></div>
<div class="paragraph"><p>Somewhat surprisingly, <code>super</code> is a bit controversial in the Python
community, there have been blog posts and flame wars on whether it is a
good thing or bad thing. Most of that centers around complex inheritance
trees, especially multiple inheritance. I see no reason not to use it as
described in this post. If things get more complicated, you do need to
make sure the arguments line up between caller and callee, and because
<code>super</code> doesn’t tell you what the callee is, that is work you have to
take care of manually.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_summing_up">Summing Up</h2>
<div class="sectionbody">
<div class="paragraph"><p>If you’ve read this far, it may have occurred that Python classes are
very flexible. This may be unlike the rather rigid contstraints of more
classical object-oriented languages. Here is a concept to think about:</p></div>
<div class="quoteblock">
<div class="content">
<div class="paragraph"><p>Classes in Python do not so much inherit, override, extend as they
delegate and reuse. You derive from another class in order to reuse
its code, which in practice means your dictionary of methods and data
is pre-populated with references from the class(es) you are "deriving"
from. But you are completely in charge of this relationship. If you
define a name that is also defined above you in your inheritance tree,
your definition is the one that goes in the dictionary, and you are
stating you want to do that work yourself, not delegate it. You can
choose, in your implementation, to call out to another implementation
for the same name from a base class - and that is not so much extending
as augmenting your version with code found elsewhere. And if you do not
define it, you are reusing the code for that name as defined elsewhere.</p></div>
</div>
<div class="attribution">
</div></div>
</div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2018-10-06 17:03:08 MDT
</div>
</div>
</body>
</html>
Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com0tag:blogger.com,1999:blog-5018861242355523355.post-11962502819026098352017-06-06T12:12:00.000-07:002017-06-06T12:15:03.267-07:00Python Test Driven Development Basics with PyTest<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Python Test Driven Development Basics with PyTest</title>
<link rel="stylesheet" href="../_static/alabaster.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '../',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head>
<body role="document">
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="python-test-driven-development-basics-with-pytest">
<h1>Python Test Driven Development Basics with PyTest<a class="headerlink" href="#python-test-driven-development-basics-with-pytest" title="Permalink to this headline">ΒΆ</a></h1>
<div class="section" id="introduction">
<h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline"></a></h2>
<p>Not long ago I was chatting with someone about some code he was working
on that did something some might consider 'obscure';, and how to be sure
it worked correctly. You can of course put in print statements to trace
what is going on, then remove them later, but this also sounds like
a case for writing a test that confirms the behavior, then coding up
the function, mkaing adjustments until the test passes. This roughly,
is what is called test-driven development (TDD).</p>
<p>When we chatted about how to do that, the person said they had trouble
working with the Python unittest module, and I pointed out I don't
much care for it either. One reason is because it forces you to use
classes even when it may not feel all that natural to write a class for
a particular problem. I wrote up some notes for him on using PyTest,
and then dedicded to modify those for somewhat wider sharing.</p>
<p>So here's a vaguely practical example of applying the pattern that
led to our discussion, that is how to effectively run a test function
several different ways, rather than writing up a function for each
permutation of your test.</p>
<p>First, and immediately violating the principles of TDD which say to
write the test first, let's write the function to test. Our candidate
function tries to return the reverse of its argument, to keep it simple,
we will assume the argument is something that can be iterated over,
so we can use fancy list slicing (thus we can't reverse a dictionary -
but that has no meaning anyway since a dictionary has no order). To show
it's working there is also code to try it out if it is called as a program
(as opposed to a module). This is the way people used to write tests for
a Python module, before test harnesses became widely used: just code up
a few checks and stuff them in the "main" part.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">slicerev</span><span class="p">(</span><span class="n">collection</span><span class="p">):</span>
<span class="k">return</span> <span class="n">collection</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="nb">print</span> <span class="n">slicerev</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">])</span>
<span class="nb">print</span> <span class="n">slicerev</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span> <span class="n">slicerev</span><span class="p">(</span><span class="s1">'abcd'</span><span class="p">)</span>
</pre></div>
</div>
<p>If we run that, we see that all of list, tuple and string did indeed
get reversed as we expected:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">dcba</span>
</pre></div>
</div>
</div>
<div class="section" id="using-pytest">
<h2>Using PyTest<a class="headerlink" href="#using-pytest" title="Permalink to this headline"></a></h2>
<p>Let's take this basic test code and turn it into a separate test using
PyTest. Unit tests for a particular function are often named (this is
convention) test_{funcname}.py. If it's named this way pytest can find
it automatically - runing py.test without arguments lets it hunt for
files that begin with 'test'. It's not mandatory to use this naming,
you can give the name of the test file as an argument, or use other
methods to describe exactly where the tests should be picked up from.</p>
<p>The code can be really simple since this is a contrived example - we're
not really systematically "unit testing", we're spot checking. All we
have to do is import the function we are going to test (even this is
not needed if the test is in the same file as the code being tested,
as opposed to a separate file), and then write out our three tests cases,
which do nothing but call the function with a known argument, then
compare the return with what we expect the result to be.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">reverser</span> <span class="k">import</span> <span class="n">slicerev</span>
<span class="k">def</span> <span class="nf">test_slicerev_list</span><span class="p">():</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">slicerev</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">])</span>
<span class="k">assert</span> <span class="n">output</span> <span class="o">==</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">test_slicerev_tuple</span><span class="p">():</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">slicerev</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">))</span>
<span class="k">assert</span> <span class="n">output</span> <span class="o">==</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_slicerev_string</span><span class="p">():</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">slicerev</span><span class="p">(</span><span class="s1">'abcd'</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">output</span> <span class="o">==</span> <span class="s1">'edcba'</span>
</pre></div>
</div>
<p>That's really all there is to it.</p>
<p>To make things a little more interesting, I have introduced an error in
the test itself: the function checking the reversed string claims it
expects 'edcba' instead of 'dcba'. This is done to show what it looks
like when PyTest reports a failure.</p>
<p>Let's run it:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>$ py.test test_slicerev.py
============================= test session starts ==============================
platform linux2 -- Python 2.7.13, pytest-2.9.2, py-1.4.33, pluggy-0.3.1
rootdir: /home/mats/PyBlog/pytester.d, inifile:
collected 3 items
test_slicerev.py ..F
=================================== FAILURES ===================================
_____________________________ test_slicerev_string _____________________________
def test_slicerev_string():
output = slicerev('abcd')
> assert output == 'edcba'
E assert 'dcba' == 'edcba'
E - dcba
E + edcba
E ? +
test_slicerev.py:13: AssertionError
====================== 1 failed, 2 passed in 0.01 seconds ======================
</pre></div>
</div>
<p>A run with that problem fixed is considerably quieter:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>$ py.test test_slicerev.py
============================= test session starts ==============================
platform linux2 -- Python 2.7.13, pytest-2.9.2, py-1.4.33, pluggy-0.3.1
rootdir: /home/mats/PyBlog/pytester.d, inifile:
collected 3 items
test_slicerev.py ...
=========================== 3 passed in 0.00 seconds ==========================
</pre></div>
</div>
</div>
<div class="section" id="pytest-fixtures">
<h2>PyTest Fixtures<a class="headerlink" href="#pytest-fixtures" title="Permalink to this headline"></a></h2>
<p>If you think about this for a bit, you notice that the same code is
run three times, only the data in the three test functions differs.
As mentioned above, this is a very common situation in testing, where
you want to try different cases to see how a unit behaves - test the
boundary conditions, test invalid data or data types, etc.</p>
<p>PyTest provides a mechanism called a "fixture" - a fixed baseline that
can be executed repeatedly, which helps with this situation.</p>
<p>In the first iteration of our tests, we did not need to import "pytest"
for it to work when the test is run by PyTest - PyTest wraps the code and
the code itself never uses anything from PyTest. However, in our second
iteration, we do want something from PyTest namespace - the definition
of the decorator we need to turn something into a PyTest fixture, so
the import is needed.</p>
<p>Since what we're factoring here is supplying different sets of data, the
fixture function 'slicedata' itself is extremely simple: all it does is
return the data. The test function has the same two functional statements
that each of the test functions had before - call the function under test,
then use an assertion to check the result was as expected. In addition
to that, the takes the fixture function as an argument, which would not
make much sense by itself, but once it is turned into a fixture it does.</p>
<p>We use a decorator to turn 'slicedata' into a fixture - remember Python
decorators are a piece of special syntax that helps alter the behavor
of a function. The PyTest fixture decorator can take a "params" parameter,
which should be something that can be iterated over, the fixture function
can then receive the data one at a time. In this case we are going
to pass a list of tuples, the first element of each tuple being the
data we are going to apply to the test, the second element being the
expected value.</p>
<p>We now know the other change we need to make to the test function:
the "fixture object" returned by the fixture will be a tuple, so
we should unpack the tuple into the pieces we want.</p>
<p>The new code looks like this:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pytest</span>
<span class="kn">from</span> <span class="nn">reverser</span> <span class="k">import</span> <span class="n">slicerev</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">params</span><span class="o">=</span><span class="p">[</span>
<span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">]),</span>
<span class="p">((</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">)),</span>
<span class="p">(</span><span class="s1">'abcd'</span><span class="p">,</span> <span class="s1">'dcba'</span><span class="p">)</span>
<span class="p">])</span>
<span class="k">def</span> <span class="nf">slicedata</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">param</span>
<span class="k">def</span> <span class="nf">test_slicerev</span><span class="p">(</span><span class="n">slicedata</span><span class="p">):</span>
<span class="nb">input</span><span class="p">,</span> <span class="n">expected</span> <span class="o">=</span> <span class="n">slicedata</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">slicerev</span><span class="p">(</span><span class="nb">input</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">output</span> <span class="o">==</span> <span class="n">expected</span>
</pre></div>
</div>
<p>Run these tests and we'll see the results are the same as before:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>$ py.test test_slicerev_fix.py
============================= test session starts ==============================
platform linux2 -- Python 2.7.13, pytest-2.9.2, py-1.4.33, pluggy-0.3.1
rootdir: /home/mats/PyBlog/pytester.d, inifile:
collected 3 items
test_slicerev_fix.py ...
=========================== 3 passed in 0.00 seconds ===========================
</pre></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="../index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Python Test Driven Development Basics with PyTest</a><ul>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#using-pytest">Using PyTest</a></li>
<li><a class="reference internal" href="#pytest-fixtures">PyTest Fixtures</a></li>
</ul>
</li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="../_sources/pytester.d/tdd-pytest.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3>Quick search</h3>
<form class="search" action="../search.html" method="get">
<div><input type="text" name="q" /></div>
<div><input type="submit" value="Go" /></div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
©2017, Mats Wichmann.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.2</a>
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
<a href="../_sources/pytester.d/tdd-pytest.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>
Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com0tag:blogger.com,1999:blog-5018861242355523355.post-898588943983599222017-06-06T12:04:00.000-07:002017-06-06T12:05:15.308-07:00Properties in Python<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Properties in Python</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
/*
pygmentize filter
*/
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f4f4f4; }
.highlight .c { color: #008800; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #008800 } /* Comment.Preproc */
.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */
.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #808080 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */
.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BB4444 } /* Literal.String */
.highlight .na { color: #BB4444 } /* Name.Attribute */
.highlight .nb { color: #AA22FF } /* Name.Builtin */
.highlight .nc { color: #0000FF } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #00A000 } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #B8860B } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */
.highlight .sc { color: #BB4444 } /* Literal.String.Char */
.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BB4444 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BB4444 } /* Literal.String.Single */
.highlight .ss { color: #B8860B } /* Literal.String.Symbol */
.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */
.highlight .vc { color: #B8860B } /* Name.Variable.Class */
.highlight .vg { color: #B8860B } /* Name.Variable.Global */
.highlight .vi { color: #B8860B } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<script type="text/javascript">
/*<+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>
" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>
";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install();
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>
Properties in Python</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
Encapsulation is an important concept in object-oriented programming.
Encapsulation just means that some information is not available directly
from outside the encapsulating object, only through specially provided
accessor methods. This sounds like a pretty good idea at first glance:
the <em>implementation</em> of something is hidden, and is allowed to change
without breaking things, because people using this code arenβt able to
poke around inside and count on details theyβve discovered, they have
to use the accessor methods.</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_is_a_property">
What is a property?</h2>
<div class="sectionbody">
<div class="paragraph">
Hereβs a contrived example, a fragmentary class (in Java, perhaps) describing
a person, where weβve only shown one piece of data, the age of the person:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="kd">class</span> <span class="nc">Person</span>
<span class="o">{</span>
<span class="kt">int</span> <span class="n">age</span><span class="o">;</span>
<span class="kd">public</span><span class="o">:</span>
<span class="n">Person</span><span class="o">()</span> <span class="o">:</span> <span class="n">age</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">{</span> <span class="o">}</span>
<span class="kt">int</span> <span class="nf">getAge</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">age</span><span class="o">;</span> <span class="o">}</span>
<span class="kt">void</span> <span class="nf">setAge</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">age</span> <span class="o">=</span> <span class="n">x</span><span class="o">;</span> <span class="o">}</span>
<span class="o">};</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
Here the instance variable <code>age</code> is private, but weβve also
provided a pair of public methods which give access to the value of age:
<code>getAge</code> to read it and <code>setAge</code> to set it.
Now if something a lot more complicated needs to happen later to <code>age</code>
the getter/setter methods can be updated but it doesnβt affect clients
using this code. And some evolution is not not actually an unreasonable
expectation - the age of a person isnβt a good candidate for data that
looks static, since every year on their birthday it changes. So perhaps
someday weβll improve the class by calculating the age from the current
date and the personβs birth date only when it is needed?</div>
<div class="paragraph">
A lot of developers donβt like this kind of code; it does accomplish a
useful function if you need it, but because you canβt necessarily guess
when youβre going to need it, when youβre writing in a language like Java,
and to a slightly lesser extent C++, you have to set up the getter/setter
methods for all data members that are to look public up front, in anticipation
that you might need the accessors later - since the interface has a
binary (ABI) nature, you canβt change from a public instance variable
to a private one with a getter/setter or you will break programs depending
on the older class signature.
This bloats the code in anticipation of something that might not
actually needed, and no new value has been introduced by all those
extra lines. Ah, but no problem, right? My IDE just auto-built
those for meβ¦</div>
<div class="paragraph">
Thereβs an aesthetic concern regarding the syntax, also. A <code>Person</code> instance
includes that personβs age, but we canβt perform natural operations on
that age - if <code>person</code> is an instance, we canβt access <code>person.age</code> or
set it, we have to use <code>person.getAge()</code> and <code>person.setAge()</code>.</div>
<div class="paragraph">
The C# language improves on this by providing properties.
C# defines properties thus:</div>
<div class="quoteblock">
<div class="content">
<div class="paragraph">
A property is a member that provides a flexible mechanism to read, write,
or compute the value of a private field. Properties can be used as if
they are public data members, but they are actually special methods
called accessors. This enables data to be accessed easily and still
helps promote the safety and flexibility of methods.</div>
</div>
<div class="attribution">
</div>
</div>
<div class="paragraph">
A simple example looks like this:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="k">class</span> <span class="nc">Person</span>
<span class="p">{</span>
<span class="k">private</span> <span class="kt">int</span> <span class="n">age</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Age</span>
<span class="p">{</span>
<span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="n">age</span><span class="p">;</span> <span class="p">}</span>
<span class="k">set</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
So if <code>person</code> is an instance of <code>Person</code>, <code>person.Age</code> (but not
<code>person.age</code>) can be accessed externally as if it were a variable.
That leads to the ability to write the much nicer</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">person</span><span class="p">.</span><span class="n">Age</span> <span class="p">+=</span> <span class="m">1</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
instead of</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">person</span><span class="p">.</span><span class="n">setAge</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">getAge</span><span class="p">()</span> <span class="p">+</span> <span class="m">1</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_properties_in_python">
Properties in Python</h2>
<div class="sectionbody">
<div class="paragraph">
Python has properties too, but there is another benefit in
Python: as a dynamic language, it does not have the limitation of
static languages, you can change the implementation,
without causing problems to clients because youβre not dealing
with a compiled interface. This means you can define an instance
variable first, then evolve it to a property later if needed,
and it will not break clients.</div>
<div class="paragraph">
Here is a series of examples showing how properties work in Python.</div>
<div class="paragraph">
Consider a Vector class that should be able to provide both an angle in
radians and an angle in degrees. This provides an excuse to
use a getter method - we donβt actually need to store both angles
in the instance, and indeed we donβt really want to, because theyβre
related, and if someone updates one angle, we have a problem
because the other one needs to change in sync with it. Itβs
nicer to store one, and generate the other one on demand - that
solves the sync problem.</div>
<div class="sect2">
<h3 id="_using_the_code_property_code_function">
Using the <code>property</code> function</h3>
<div class="paragraph">
Python provides the built-in <code>property()</code> function which sets
up a property given arguments which describe the methods which
implement the property behavior. The arguments are in order
are the getter, setter, deleter, and docstring; theyβre
successively optional so if you pass only one argument to
property only a getter is assigned.</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="kn">import</span> <span class="nn">math</span>
<span class="k">class</span> <span class="nc">Vector</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">angle_rad</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">set_angle_rad</span><span class="p">(</span><span class="n">angle_rad</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_angle_rad</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">radians</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set_angle_rad</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">angle_rad</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">degrees</span><span class="p">(</span><span class="n">angle_rad</span><span class="p">)</span>
<span class="n">angle</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">get_angle_rad</span><span class="p">,</span> <span class="n">set_angle_rad</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_angle_deg</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span>
<span class="k">def</span> <span class="nf">set_angle_deg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">angle_deg</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span> <span class="o">=</span> <span class="n">angle_deg</span>
<span class="n">angle_deg</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">get_angle_deg</span><span class="p">,</span> <span class="n">set_angle_deg</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
We can do some experiments with this class - in the first
set of lines below we create an instance with a starting value
and print both angles, then change the first the <code>angle</code>
then the <code>angle_deg</code> values to show theyβre working in unison.
In the final chunk, we ask for some information
about the objects in question to illustrate how Python
has set this up.</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">v</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">math</span><span class="o">.</span><span class="n">pi</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span> <span class="o">=</span> <span class="mf">90.0</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="o">.</span><span class="n">getter</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="o">.</span><span class="n">setter</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="o">.</span><span class="n">getter</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="o">.</span><span class="n">setter</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
Hereβs the output of one run:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">Rad</span><span class="p">:</span> <span class="mf">6.283185307179586</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">360.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">3.141592653589793</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">180.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">1.5707963267948966</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">90.0</span>
<span class="o"><</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab853b5f48</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">getter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab853b5f48</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">setter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab853b5f48</span><span class="o">></span>
<span class="o"><</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab7d3d9818</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">getter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab7d3d9818</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">setter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab7d3d9818</span><span class="o">></span>
</pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_using_the_property_decorators">
Using the property decorators</h3>
<div class="paragraph">
Python provides decorators that have the same effect as
the the call to the <code>property</code> function. <code>@property</code> is used
for the getter, <code>@x.setter</code> for the setter and
<code>@x.deleter</code> for the deleter method which would be
the third argument to the <code>property</code> function if included
(replace <code>x</code> with the method name).</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="kn">import</span> <span class="nn">math</span>
<span class="k">class</span> <span class="nc">Vector</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">angle</span> <span class="o">=</span> <span class="n">value</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">angle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">radians</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span><span class="p">)</span>
<span class="nd">@angle.setter</span>
<span class="k">def</span> <span class="nf">angle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">degrees</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">angle_deg</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span>
<span class="nd">@angle_deg.setter</span>
<span class="k">def</span> <span class="nf">angle_deg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_angle_deg</span> <span class="o">=</span> <span class="n">value</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">math</span><span class="o">.</span><span class="n">pi</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span> <span class="o">=</span> <span class="mf">90.0</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="o">.</span><span class="n">getter</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle</span><span class="o">.</span><span class="n">setter</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="o">.</span><span class="n">getter</span><span class="p">,</span> <span class="n">Vector</span><span class="o">.</span><span class="n">angle_deg</span><span class="o">.</span><span class="n">setter</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
And the output of our experiments:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">Rad</span><span class="p">:</span> <span class="mf">6.283185307179586</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">360.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">3.141592653589793</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">180.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">1.5707963267948966</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">90.0</span>
<span class="o"><</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5818</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">getter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5818</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">setter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5818</span><span class="o">></span>
<span class="o"><</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5868</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">getter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5868</span><span class="o">></span>
<span class="o"><</span><span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">method</span> <span class="n">setter</span> <span class="n">of</span> <span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5868</span><span class="o">></span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
By decorating the <code>angle</code> and <code>angle_deg</code> method pairs, weβve
turned them into properties with getter/setter methods,
just like the call to the <code>property</code> function did,
but this looks cleaner, you can immediately see what each
method is for rather than going hunting to see theyβre
later part of a <code>property</code> call. Notice that the method names have to
be the same for all the parts of the property; for the setter and
deleter the decorator also takes the name of the method.</div>
</div>
<div class="sect2">
<h3 id="_code_simplification">
Code Simplification</h3>
<div class="paragraph">
I donβt particularly like this code, though. We are using a sort of
hidden instance variable as the backing field which holds the value,
and weβve served up getter/setter pairs for both public variables.
Except there is really no hidden data in Python - starting a name with
an underscore is a visual hint that we donβt intend something to be
public, but that is all it is, a hint (a leading single underscore only
"matters" in imports). That means someone could actually fiddle directly
with the backing field <code>_angle_deg</code>, bypassing the getter/setter,
if they were so motivated. In the trivial example here, that doesnβt
introduce any new problems, but in a setter which does a bunch of
validation so you know an invalid value is never stored, it is not ideal.
And in fact, that the setter for <code>angle_deg</code> does not do anything
special is my other complaint: why implement a getter/setter when
there is no need to?</div>
<div class="paragraph">
So why not undo the property definition that does not seem needed
and just make <code>angle_deg</code> an instance variable, then we donβt need
<code>_angle_deg</code> at all. If we find we need to do something "special" with
<code>angle_deg</code> later we can always turn it back into a property.
Notice in the initializer, we are invoking the property setter,
because we assign to <code>angle</code>. As a next refactor, I would probably
turn this around and use the radians form as the instance
variable to make it all feel more natural. This is the
Python flexibility I was referring to at the beginning of this article.
Hereβs the refactored code, which is now quite a bit shorter:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="kn">import</span> <span class="nn">math</span>
<span class="k">class</span> <span class="nc">Vector</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">angle</span> <span class="o">=</span> <span class="n">value</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">angle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">radians</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">)</span>
<span class="nd">@angle.setter</span>
<span class="k">def</span> <span class="nf">angle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">angle_deg</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">degrees</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
<span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span> <span class="o">=</span> <span class="mf">90.0</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Rad: {}, Deg: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">angle</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">angle_deg</span><span class="p">))</span>
</pre>
</div>
</div>
</div>
<div class="paragraph">
This works just the same, as we see from the output:</div>
<div class="listingblock">
<div class="content">
<div class="highlight">
<pre><span></span><span class="n">Rad</span><span class="p">:</span> <span class="mf">6.283185307179586</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">360.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">3.141592653589793</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">180.0</span>
<span class="n">Rad</span><span class="p">:</span> <span class="mf">1.5707963267948966</span><span class="p">,</span> <span class="n">Deg</span><span class="p">:</span> <span class="mf">90.0</span>
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr />
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-02-24 13:16:42 MST
</div>
</div>
</body>
</html>
Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com1tag:blogger.com,1999:blog-5018861242355523355.post-5882178636514620562017-02-20T07:59:00.001-08:002017-02-23T14:11:49.114-08:00Can Python Overload Methods?<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.6.8" />
<title>Can Python Overload Methods?</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
/*
pygmentize filter
*/
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f4f4f4; }
.highlight .c { color: #008800; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #008800 } /* Comment.Preproc */
.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */
.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #808080 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */
.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BB4444 } /* Literal.String */
.highlight .na { color: #BB4444 } /* Name.Attribute */
.highlight .nb { color: #AA22FF } /* Name.Builtin */
.highlight .nc { color: #0000FF } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #00A000 } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #B8860B } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */
.highlight .sc { color: #BB4444 } /* Literal.String.Char */
.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BB4444 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BB4444 } /* Literal.String.Single */
.highlight .ss { color: #B8860B } /* Literal.String.Symbol */
.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */
.highlight .vc { color: #B8860B } /* Name.Variable.Class */
.highlight .vg { color: #B8860B } /* Name.Variable.Global */
.highlight .vi { color: #B8860B } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<script type="text/javascript">
/*<+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install();
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>Can Python Overload Methods?</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>The title of the post is a question that seems to come up quite often
in beginner forums, you can find a lot of hits in a web search,
on Stackoverflow, etc.
Not surprisingly, it usually comes from people who have worked
in languages where method/function overloading is a big part of the
language design.</p></div>
<div class="paragraph"><p>According to Wikipedia,</p></div>
<div class="quoteblock">
<div class="content">
<div class="paragraph"><p>…function overloading or method overloading is the ability to create
multiple methods of the same name with different implementations.
Calls to an overloaded function will run a specific implementation
of that function appropriate to the context of the call, allowing one
function call to perform different tasks depending on context.</p></div>
</div>
<div class="attribution">
</div></div>
<div class="paragraph"><p>Sometimes this question ends up dismissed as stupid in the Python
context, “because you don’t need that in Python”.
I’d like to take some time to talk about
that, because the ideas behind it aren’t worthless.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_arguments_of_differing_types">Arguments of Differing Types</h2>
<div class="sectionbody">
<div class="paragraph"><p>As a simplistic example, consider a class which should work whether
an argument to a class constructor is an integer or a double-precision</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">Foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">);</span>
<span class="n">Foo</span><span class="p">(</span><span class="kt">double</span> <span class="n">x</span><span class="p">);</span>
<span class="p">...</span>
<span class="p">};</span>
<span class="n">Foo</span><span class="o">::</span><span class="n">Foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
<span class="n">Foo</span><span class="o">::</span><span class="n">Foo</span><span class="p">(</span><span class="kt">double</span> <span class="n">x</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
<span class="n">Foo</span> <span class="n">a</span> <span class="o">=</span> <span class="k">new</span><span class="p">(</span><span class="n">Foo</span><span class="p">(</span><span class="mi">12</span><span class="p">));</span>
<span class="n">Foo</span> <span class="n">b</span> <span class="o">=</span> <span class="k">new</span><span class="p">(</span><span class="n">Foo</span><span class="p">(</span><span class="mf">18.0</span><span class="p">));</span>
</pre></div></div></div>
<div class="paragraph"><p>So, two different constructor functions both named after the class,
as that is the C++ syntax. C++ needs that information up front, or
the compiler will throw type errors on compilation, as it’s an error to
pass an argument of one type to a function expecting a different type.
Overloading is often discussed in terms of constructors, though it
is not limited to those methods.</p></div>
<div class="paragraph"><p>Python syntax does not allow multiple functions/methods to take the
same name within the same scope - just like with variables.
People are sometimes a little surprised at this, but
a Python function definition is an executable statement, in which
two things happen: a function object is created from the body of
the definition, and a reference to that object is then assigned to
a variable with the name of the function - in other words, a
<code>def</code> statement is variable assignment. So assigning two different
functions to the same name just means the second one replaces the first.
It’s "override" rather than "overload".
In the case of Python, the method we are interested in is <code>__init__</code>.
It is not exactly a constructor, but as the method called automatically
after construction, the behavior that people associate with
constructors (“set things up”) happens here.</p></div>
<div class="paragraph"><p>We can show this sequence with a bit of interactive Python use:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="o">>>></span> <span class="n">a</span><span class="o">=</span><span class="mi">2</span>
<span class="o">>>></span> <span class="n">a</span><span class="o">=</span><span class="mi">4</span>
<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="mi">4</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">a</span><span class="p">():</span>
<span class="o">...</span> <span class="k">print</span> <span class="s2">"Hello"</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="o"><</span><span class="n">function</span> <span class="n">a</span> <span class="n">at</span> <span class="mh">0x7f7fa29b4c80</span><span class="o">></span>
<span class="o">>>></span> <span class="n">a</span><span class="p">()</span>
<span class="n">Hello</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">a</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="o">...</span> <span class="k">print</span> <span class="s2">"Hi, argument was"</span><span class="p">,</span> <span class="n">x</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="o"><</span><span class="n">function</span> <span class="n">a</span> <span class="n">at</span> <span class="mh">0x7f7fa29b4cf8</span><span class="o">></span> <span class="c1"># <b><1></b></span>
<span class="o">>>></span> <span class="n">a</span><span class="p">()</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"<stdin>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span>
<span class="ne">TypeError</span><span class="p">:</span> <span class="n">a</span><span class="p">()</span> <span class="n">takes</span> <span class="n">exactly</span> <span class="mi">1</span> <span class="n">argument</span> <span class="p">(</span><span class="mi">0</span> <span class="n">given</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">a</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="n">Hi</span><span class="p">,</span> <span class="n">argument</span> <span class="n">was</span> <span class="mi">8</span>
<span class="o">>>></span>
</pre></div></div></div>
<div class="colist arabic"><ol>
<li>
<p>
note different address from previous function object
</p>
</li>
</ol></div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph"><p>As an aside there does turn out to be a place where you can
apparently have multiple method definitions of the same name,
and that is when defining properties (getter/setter) - these
are written decorated and are transformed by Python so they’re
not really the same name, but they look that way in your source
code. That’s a topic for another time, though.</p></div>
</div></div>
<div class="paragraph"><p>So while you can not define multiple functions/methods of the
same name, since Python is not a strongly typed language, that is
not necessarily any problem. Consider the following snippet:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mf">18.0</span><span class="p">)</span>
<span class="n">c</span> <span class="o">-</span> <span class="n">Foo</span><span class="p">(</span><span class="s2">"text"</span><span class="p">)</span>
</pre></div></div></div>
<div class="paragraph"><p>That is, we’ve instantiated with three different types of arguments,
but the initializer doesn’t look any different.
Of course, you have to make sure the method you write in Python can
actually handle arguments of different types. Python <em>will</em> let you
check the type of an object, so you could go ahead and carefully check
your arguments and make sure:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="o">>>></span> <span class="n">x</span> <span class="o">=</span> <span class="mi">12</span>
<span class="o">>>></span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="bp">True</span>
<span class="o">>>></span> <span class="n">x</span> <span class="o">=</span> <span class="mf">18.0</span>
<span class="o">>>></span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="bp">False</span>
<span class="o">>>></span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nb">float</span><span class="p">)</span>
<span class="bp">True</span>
</pre></div></div></div>
<div class="paragraph"><p>But there’s a concept often cited in Python circles that it’s "Easier to
Ask Forgiveness than Permission" (or EAFP), which suggests that you just
go ahead and try something, and clean up the mess afterwards if it didn’t
work. That’s antithetical to some software development theory which says
to check everything first ("Look Before You Leap"), but as long as you
can properly deal with the fallout from failures there’s not any real
problem. That idea isn’t unique to Python, though it may be expressed
in different ways when talking about different languages. Exceptions are
an example of this way of thinking, and they exist in many languages.
This isn’t just a case of dogma, consider that many LBYL sequences
have proven to have timing holes that lead to security issues - for
example if you write code to validate a filename path, then proceed to
open it after the check completes, the time difference between the two,
however minimal, is a window where someone external to the program can
link the pathname elsewhere and cause the program to open something it
didn’t intend to. This class of security issues is called TOCTOU (Time
Of Check, Time Of Use).</p></div>
<div class="paragraph"><p>And it turns out Python can often handle differing types without
any problem - it’s not an error, for example, to perform arithmetic
operations between an integer and a float. That gets into another concept
often discussed around Python, "duck typing". That is a term which is
intentionally a little silly:</p></div>
<div class="quoteblock">
<div class="content">
<div class="paragraph"><p>In other words, don’t check whether it IS-a duck: check whether it
QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what
subset of duck-like behavior you need to play your language-games with.</p></div>
</div>
<div class="attribution">
<em>comp.lang.python</em><br />
— "Alex Martelli"
</div></div>
<div class="paragraph"><p>In this simple case, if your objective is to perform arithmetic on your
arguments, then integers and floats do both have methods like add, subtract,
multiply, etc. So there is not a compelling reason to treat them in
different ways unless you actually run into a case where behavior
is incompatible with your expectations.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_differing_numbers_of_arguments">Differing Numbers of Arguments</h2>
<div class="sectionbody">
<div class="paragraph"><p>Another case for overloading in static languages is if the method
may need to take different numbers of arguments. This can come up
in a few different ways, to list a couple of examples:</p></div>
<div class="ulist"><ul>
<li>
<p>
You want to offer different ways to instantiate a class, as in a
hypothetical employee database where a new employee can be added by a
(Firstname, Lastname, Salary) triple, or by a string encoding all
three as "Firstname-Lastname-Salary".
</p>
</li>
<li>
<p>
API evolution: say you’ve implemented a class, and then later find out
you need to make some extensions to your API which involves passing an
additional parameter. If you just change the constructor, then all the
code instantiating that class must now change. But by overload through
adding a new constructor plus leaving the old one and adjusting its
behavior so it has a sensible default if the added argument from the
new constructor is not passed old and new code can both be supported.
</p>
</li>
</ul></div>
<div class="sect2">
<h3 id="_api_evolves_arguments_added">API Evolves, Arguments Added</h3>
<div class="paragraph"><p>Of the two examples, the "we added an argument but don’t want to break
backwards compatibility" case seems fairly easy to handle in Python. A
combination of keyword arguments and/or default arguments normally does
the trick. So we can go from:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
</pre></div></div></div>
<div class="paragraph"><p>to:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span> <span class="c1"># <b><1></b></span>
<span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span> <span class="c1"># <b><2></b></span>
<span class="n">b</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mf">18.0</span><span class="p">)</span> <span class="c1"># <b><3></b></span>
</pre></div></div></div>
<div class="colist arabic"><ol>
<li>
<p>
Even if <code>y</code> was not passed, this is okay since it is set to
default to something (<code>None</code> in this case).
</p>
</li>
<li>
<p>
Old way, one argument, still works
</p>
</li>
<li>
<p>
New way, two arguments
</p>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_differing_class_instantiations">Differing Class Instantiations</h3>
<div class="paragraph"><p>The other example case has some more nuances. It suggests we’re intending,
up front, to allow the class to instantiated in quite different
ways (although this change could of course also happen as part of an
evolution)</p></div>
<div class="paragraph"><p>One way to approach this case is to use
Python’s keyword argument passing. Rather than trying to
put this in words, here’s an example:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Employee</span><span class="p">:</span>
<span class="n">num_of_emps</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="s2">"emp_str"</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">:</span>
<span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">pay</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"emp_str"</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)</span>
<span class="k">elif</span> <span class="s2">"first"</span> <span class="ow">in</span> <span class="n">kwargs</span> <span class="ow">and</span> <span class="s2">"last"</span> <span class="ow">in</span> <span class="n">kwargs</span> <span class="ow">and</span> <span class="s2">"pay"</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">:</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"first"</span><span class="p">]</span>
<span class="n">last</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"last"</span><span class="p">]</span>
<span class="n">pay</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"pay"</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"invalid initializer:"</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">first</span> <span class="o">=</span> <span class="n">first</span>
<span class="bp">self</span><span class="o">.</span><span class="n">last</span> <span class="o">=</span> <span class="n">last</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pay</span> <span class="o">=</span> <span class="n">pay</span>
<span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">"Name: {} {}, Pay: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">first</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">last</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">pay</span><span class="p">)</span>
<span class="n">emp_1</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">(</span><span class="n">first</span><span class="o">=</span><span class="s2">"John"</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">"Public"</span><span class="p">,</span> <span class="n">pay</span><span class="o">=</span><span class="mi">50000</span><span class="p">)</span> <span class="c1"># <b><1></b></span>
<span class="n">emp_2</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">(</span><span class="n">emp_str</span><span class="o">=</span><span class="s2">"Test-Employee-60000"</span><span class="p">)</span> <span class="c1"># <b><2></b></span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Employees:"</span><span class="p">,</span> <span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span><span class="p">)</span>
</pre></div></div></div>
<div class="colist arabic"><ol>
<li>
<p>
Pass a tuple of values
</p>
</li>
<li>
<p>
Pass a string encoding all the values
</p>
</li>
</ol></div>
<div class="paragraph"><p>We have managed to instantiate an Employee two ways: by
passing a tuple of values, or by passing an encoded string.
In the initializer, we try to work out which way we were
called by digging around in the dictionary that is given
to us as <code>kwargs</code>, then fishing the actual values out
of that dict, and saving them in instance variables.
So this is certainly a form of "overloading", though it
feels kind of clunky.</p></div>
<div class="paragraph"><p>We might as well try using default values instead:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Employee</span><span class="p">:</span>
<span class="n">num_of_emps</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pay</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">emp_str</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">emp_str</span><span class="p">:</span>
<span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">pay</span> <span class="o">=</span> <span class="n">emp_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="p">(</span><span class="n">first</span> <span class="ow">and</span> <span class="n">last</span> <span class="ow">and</span> <span class="n">pay</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"invalid initializer"</span><span class="p">)</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">first</span> <span class="o">=</span> <span class="n">first</span>
<span class="bp">self</span><span class="o">.</span><span class="n">last</span> <span class="o">=</span> <span class="n">last</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pay</span> <span class="o">=</span> <span class="n">pay</span>
<span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">"Name: {} {}, Pay: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">first</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">last</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">pay</span><span class="p">)</span>
<span class="n">emp_1</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">(</span><span class="n">first</span><span class="o">=</span><span class="s2">"John"</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">"Public"</span><span class="p">,</span> <span class="n">pay</span><span class="o">=</span><span class="mi">50000</span><span class="p">)</span> <span class="c1"># <b><1></b></span>
<span class="n">emp_2</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">(</span><span class="n">emp_str</span><span class="o">=</span><span class="s2">"Test-Employee-60000"</span><span class="p">)</span> <span class="c1"># <b><2></b></span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Employees:"</span><span class="p">,</span> <span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span><span class="p">)</span>
</pre></div></div></div>
<div class="colist arabic"><ol>
<li>
<p>
Pass a tuple of values
</p>
</li>
<li>
<p>
Pass a string encoding all the values
</p>
</li>
</ol></div>
<div class="paragraph"><p>Notice the caller side of this is identical - the "API" is the same.
This is a little simpler looking, but it still feels awkward
because of making assumptions in the <code>__init__</code> function,
based on possibly not terribly reliable information - in the first
example we looked for the presence of key names in a dictionary,
in this one we’re looking for non-default values of named
arguments: if the string value is present we use it, else
we check that we have all three of the expected arguments in the
other form, and go from there.</p></div>
<div class="paragraph"><p>There is another way to tackle this, which gets back to my
objective in writing these posts - learning things added to
Python since the "early days" of Python 2, and seeing how they
can be used to make code nicer looking, and that is to use
class methods. Class methods are not really new Python, they
appeared in 2.2 and the decorator form was added in 2.4. Still,
it’s not something I had learned about in those early Python 2
days.</p></div>
<div class="paragraph"><p>To know what’s going on here, when a method is defined inside
a class definition, it is by default what is called an instance
method. That means it receives an implicit first argument
which is a reference to the instance object. By convention this
argument is named <code>self</code>, though the name itself is not anything
magical. For a class method, this implicit argument is instead
a reference to the class object, and is by convention named
<code>cls</code>. The simple way to set this up is to decorate the
method definition with <code>@classmethod</code>. There is another kind
of method known as a static method, which does not receive
either an instance or class argument.</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Employee</span><span class="p">:</span>
<span class="n">num_of_emps</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">pay</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">first</span> <span class="o">=</span> <span class="n">first</span>
<span class="bp">self</span><span class="o">.</span><span class="n">last</span> <span class="o">=</span> <span class="n">last</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pay</span> <span class="o">=</span> <span class="n">pay</span>
<span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">from_string</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">emp_str</span><span class="p">):</span>
<span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">pay</span> <span class="o">=</span> <span class="n">emp_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">cls</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">pay</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">"Name: {} {}, Pay: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">first</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">last</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">pay</span><span class="p">)</span>
<span class="n">emp_1</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">(</span><span class="n">first</span><span class="o">=</span><span class="s2">"John"</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">"Public"</span><span class="p">,</span> <span class="n">pay</span><span class="o">=</span><span class="mi">50000</span><span class="p">)</span> <span class="c1"># <b><1></b></span>
<span class="n">emp_2</span> <span class="o">=</span> <span class="n">Employee</span><span class="o">.</span><span class="n">from_string</span><span class="p">(</span><span class="n">emp_str</span><span class="o">=</span><span class="s2">"Test-Employee-60000"</span><span class="p">)</span> <span class="c1"># <b><2></b></span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">emp_2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">Employee</span><span class="o">.</span><span class="n">num_of_emps</span><span class="p">)</span>
</pre></div></div></div>
<div class="colist arabic"><ol>
<li>
<p>
Pass a tuple of values
</p>
</li>
<li>
<p>
Pass a string containing all the values, using the <code>from_string</code> classmethod
</p>
</li>
</ol></div>
<div class="paragraph"><p>This leaves something nice and clean looking. I will admit for those
who come from the "method overloading" point of view, it
the way the string form is instantiated is different so it
doesn’t look quite like classical overloading any more.
Also note for symmetry, the tuple form could also
be written as a class method, with both then calling to the
initializer by calling through the class. Then at least the
invocation methods would look more similar, as in:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></span><span class="n">emp_1</span> <span class="o">=</span> <span class="n">Employee</span><span class="o">.</span><span class="n">from_tuple</span><span class="p">(</span><span class="n">first</span><span class="o">=</span><span class="s2">"John"</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">"Public"</span><span class="p">,</span> <span class="n">pay</span><span class="o">=</span><span class="mi">50000</span><span class="p">)</span>
<span class="n">emp_2</span> <span class="o">=</span> <span class="n">Employee</span><span class="o">.</span><span class="n">from_string</span><span class="p">(</span><span class="n">emp_str</span><span class="o">=</span><span class="s2">"Test-Employee-60000"</span><span class="p">)</span>
</pre></div></div></div>
</div>
</div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2017-02-23 15:09:39 MST
</div>
</div>
</body>
</html>
Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com0tag:blogger.com,1999:blog-5018861242355523355.post-41053765894932079882017-01-10T13:06:00.000-08:002017-01-27T13:58:35.143-08:00The Python "with" statement and Context Managers<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.6.8" />
<title>The Python "with" statement and Context Managers</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
</style>
<script type="text/javascript">
/*<+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install();
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>The Python "with" statement and Context Managers</h1>
</div>
<div id="content">
<div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph"><p>The Python "with" statement helps when you want to refactor code that
follows a particular pattern, roughly along these lines:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># set things up</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">try</span></span><span style="color: #990000">:</span>
<span style="font-style: italic"><span style="color: #9A1900"># do something</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">finally</span></span><span style="color: #990000">:</span>
<span style="font-style: italic"><span style="color: #9A1900"># tear things down</span></span></tt></pre></div></div>
<div class="paragraph"><p>When do you need this sort of code? When there’s some resource you
need access to, but don’t have to (and should not) hold on to forever.
The two most common cases, I believe, are file descriptors (open files)
and thread synchronization (using mutex locks). A bit of googling would
turn up lots of articles and forum posts that look like "Python whatsit
leaks file descriptors", "Python subprocess runs out of file descriptors",
etc. Managing file descriptor usage can be quite tricky if the opened
file descriptors are used all over the place; if that situation exists
perhaps some other refactoring is also in order. Locks are a little
simpler, they exist to manipulate something which could have concurrent
access problems, and programmers know that section should be as short
as possible, so the usage doesn’t tend to scatter all over the place,
but it’s still easy to get into trouble, as an example below will show.</p></div>
<div class="paragraph"><p>Here’s a simple snippet using a try/finally block to manage file opens,
this sort of thing, a decade or more ago, was a common idiom for working
with an external file.</p></div>
<div class="listingblock">
<div class="title">File Open Wrapper</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>outfile <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">open</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"foo.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"w"</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">try</span></span><span style="color: #990000">:</span>
outfile<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">write</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'foo'</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">finally</span></span><span style="color: #990000">:</span>
outfile<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">close</span></span><span style="color: #990000">()</span></tt></pre></div></div>
<div class="paragraph"><p>To be pedantic, the file open could fail, for example if the file could
already exists and not have write permission, so possibly the open should
also be wrapped in a try block.</p></div>
<div class="paragraph"><p>Anyway, if you don’t make sure a file close happens, and there are lots
of files to open, you will eventually run out of the system resource
that is file descriptors.</p></div>
<div class="paragraph"><p>Here’s a snippet using locks:</p></div>
<div class="listingblock">
<div class="title">Lock Wrapper</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #000080">from</span></span> threading <span style="font-weight: bold"><span style="color: #000080">import</span></span> Lock
lock <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">Lock</span></span><span style="color: #990000">()</span>
lock<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">acquire</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">try</span></span><span style="color: #990000">:</span>
my_list<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">append</span></span><span style="color: #990000">(</span>item<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">finally</span></span><span style="color: #990000">:</span>
lock<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">release</span></span><span style="color: #990000">()</span></tt></pre></div></div>
<div class="paragraph"><p>Handling locks carefully is really important. Here’s a different (very
artificial, and probably not too realistic) snippet that shows how to
get into trouble:</p></div>
<div class="listingblock">
<div class="title">Lock Deadlock</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">some_critical_section</span></span><span style="color: #990000">(</span>my_list<span style="color: #990000">,</span> item<span style="color: #990000">):</span>
lock<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">acquire</span></span><span style="color: #990000">()</span>
my_list<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">append</span></span><span style="color: #990000">(</span>item<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">return</span></span> <span style="color: #FF0000">'some kind of error here'</span>
lock<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">release</span></span><span style="color: #990000">()</span></tt></pre></div></div>
<div class="paragraph"><p>In other words, "something happens" somewhere between the acquire and
release, and we drop out of the function without the lock ever being
released. Future calls to this function will block trying to acquire
the lock that they’ll never get. You have to write carefully not to
get into such deadlock situations, once code complexity rises. Did you
anticipate the possible error exits, and release the lock in all of them?</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_the_with_statment">The "with" statment</h2>
<div class="sectionbody">
<div class="paragraph"><p>Both of the file open and thread lock examples follow the
pattern shown at the beginning. In Python 2.5 (as described by
<a href="https://www.python.org/dev/peps/pep-0343/">PEP 343</a>), a bit of new syntax
was introduced to help write things using this pattern more concisely,
and thus hopefully more clearly. The new keyword "with" was introduced,
and it can take a companion "as" clause. Using "with" sets up a context
that Python keeps track of, wrapping it in appropriate begin/end logic.</p></div>
<div class="paragraph"><p>Using it is pretty simple:</p></div>
<div class="listingblock">
<div class="title">Lock Wrapper Using "with"</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #000080">from</span></span> threading <span style="font-weight: bold"><span style="color: #000080">import</span></span> Lock
lock <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">Lock</span></span><span style="color: #990000">()</span>
with lock<span style="color: #990000">:</span>
my_list<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">append</span></span><span style="color: #990000">(</span>item<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>If you need a handle to the resource being acquired, which is usually
the case, you can save that by adding an "as" clause, like this:</p></div>
<div class="listingblock">
<div class="title">Open Wrapper Using "with"</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>with <span style="font-weight: bold"><span style="color: #000000">open</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"foo.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"w"</span><span style="color: #990000">)</span> as outfile<span style="color: #990000">:</span>
outfile<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">write</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'foo'</span><span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>The block following the "with" statement is the context, and Python
takes care of wrapping beginning and ending steps around that context.
The above are the new idioms for dealing with these kinds of resources,
and probably most people who have learned Python from 2.6 on know these
(I said earlier "with" was introduced in Python 2.5, but there it was
available only as a "future" feature, 2.6 is where it really became
mainstream) - but perhaps don’t understand why. The why is a cleaner
syntax and cleaner concept of what the "context" section is.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_context_managers">Context Managers</h2>
<div class="sectionbody">
<div class="paragraph"><p>Notice in the "with" versions of the examples there appear to be
details missing: in the lock example, lock.acquire() and lock.release()
are not mentioned; in the file open example the outfile.close()
is not present - which leads to the question "how does Python
know what to do here?". It turns out that using the "with"
statement requires help from something called a Context Manager,
which is a class which follows the context management protocol. The
<a href="https://docs.python.org/2/library/stdtypes.html#typecontextmanager">Python
documentation</a> describes how that works.</p></div>
<div class="paragraph"><p>The tl;dr version (but do go read the documentation to understand
more!) is that a context manager provides the methods <code>__enter__</code> and
<code>__exit__</code>, and it is these which do the work mentioned above. The the
file and thread-lock objects in Python already come as context managers,
and there are a number more.</p></div>
<div class="paragraph"><p>We could write our own example of how to handle file opens (not needed
since the Python file object is already a context manager) just to show
what a context manager looks like:</p></div>
<div class="listingblock">
<div class="title">Context Manager Example</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">File</span></span><span style="color: #990000">():</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> filename<span style="color: #990000">,</span> mode<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>filename <span style="color: #990000">=</span> filename
self<span style="color: #990000">.</span>mode <span style="color: #990000">=</span> mode
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__enter__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>open_file <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">open</span></span><span style="color: #990000">(</span>self<span style="color: #990000">.</span>filename<span style="color: #990000">,</span> self<span style="color: #990000">.</span>mode<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">return</span></span> self<span style="color: #990000">.</span>open_file
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__exit__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> <span style="color: #990000">*</span>args<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>open_file<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">close</span></span><span style="color: #990000">()</span>
with <span style="font-weight: bold"><span style="color: #000000">File</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"foo.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"w"</span><span style="color: #990000">)</span> as outfile<span style="color: #990000">:</span>
outfile<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">write</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'foo'</span><span style="color: #990000">)</span></tt></pre></div></div>
</div>
</div>
<div class="sect1">
<h2 id="_decorated_generators_as_context_managers">Decorated Generators as Context Managers</h2>
<div class="sectionbody">
<div class="paragraph"><p>We can of course write context managers in the style just shown, but often
it’s easier to write a generator function, which we can then decorate
with syntax that will intsruct Python to turn it into a context manager.
The decoration is <code>@contextlib.contextmanager</code> (you can shorten that based
on the way you import), and what happens is the code before the "yield"
statement is turned into the <code>__enter__</code> method while the code after it
is turned into the <code>__exit__</code> method.</p></div>
<div class="paragraph"><p>Let’s show how this works with a somewhat practical example: timing an
operation via a context manager. Python already provides a very nice
timing module (timeit), but using it in the manner of this example (IMHO)
makes for nice readable code. The "wrapping" behavior of the context
manager doesn’t have to be limited to critical code sections. Timing
code fits the model too: the "setup" is capturing a timestamp before
the context block runs; the "teardown" is capturing a timestamp after
it has completed, and then computing the difference (in the example we
also print out the result).</p></div>
<div class="paragraph"><p>Here is a timing context manager in class form, plus some code to do
something we can time (fetching a URL):</p></div>
<div class="listingblock">
<div class="title">Context Manager Class for Timing</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #000080">from</span></span> timeit <span style="font-weight: bold"><span style="color: #000080">import</span></span> default_timer
<span style="font-weight: bold"><span style="color: #000080">import</span></span> requests
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="font-weight: bold"><span style="color: #000000">Timer</span></span><span style="color: #990000">(</span>object<span style="color: #990000">):</span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>timer <span style="color: #990000">=</span> default_timer
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__enter__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">):</span>
self<span style="color: #990000">.</span>start <span style="color: #990000">=</span> self<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">timer</span></span><span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">return</span></span> self
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__exit__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> <span style="color: #990000">*</span>args<span style="color: #990000">):</span>
end <span style="color: #990000">=</span> self<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">timer</span></span><span style="color: #990000">()</span>
self<span style="color: #990000">.</span>elapsed_secs <span style="color: #990000">=</span> end <span style="color: #990000">-</span> self<span style="color: #990000">.</span>start
self<span style="color: #990000">.</span>elapsed <span style="color: #990000">=</span> self<span style="color: #990000">.</span>elapsed_secs <span style="color: #990000">*</span> <span style="color: #993399">1000</span> <span style="font-style: italic"><span style="color: #9A1900"># millisecs</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span> <span style="color: #FF0000">'elapsed time: %f ms'</span> <span style="color: #990000">%</span> self<span style="color: #990000">.</span>elapsed
url <span style="color: #990000">=</span> <span style="color: #FF0000">'https://github.com/timeline.json'</span>
with <span style="font-weight: bold"><span style="color: #000000">Timer</span></span><span style="color: #990000">():</span>
r <span style="color: #990000">=</span> requests<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get</span></span><span style="color: #990000">(</span>url<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>Running this, you might get something like:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>elapsed time: 375.089169 ms</code></pre>
</div></div>
<div class="paragraph"><p>Rewriting it into decorated-generator form:</p></div>
<div class="listingblock">
<div class="title">Context Manager Decorated Generator</div>
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #000080">from</span></span> timeit <span style="font-weight: bold"><span style="color: #000080">import</span></span> default_timer
<span style="font-weight: bold"><span style="color: #000080">import</span></span> requests
<span style="font-weight: bold"><span style="color: #000080">from</span></span> contextlib <span style="font-weight: bold"><span style="color: #000080">import</span></span> contextmanager
@contextmanager
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">Timer</span></span><span style="color: #990000">():</span>
start <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">default_timer</span></span><span style="color: #990000">()</span>
yield
elapsed_secs <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">default_timer</span></span><span style="color: #990000">()</span> <span style="color: #990000">-</span> start
elapsed <span style="color: #990000">=</span> elapsed_secs <span style="color: #990000">*</span> <span style="color: #993399">1000</span> <span style="font-style: italic"><span style="color: #9A1900"># millisecs</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">print</span></span> <span style="color: #FF0000">'elapsed time: %f ms'</span> <span style="color: #990000">%</span> elapsed
url <span style="color: #990000">=</span> <span style="color: #FF0000">'https://github.com/timeline.json'</span>
with <span style="font-weight: bold"><span style="color: #000000">Timer</span></span><span style="color: #990000">():</span>
r <span style="color: #990000">=</span> requests<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get</span></span><span style="color: #990000">(</span>url<span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>and this version works the same way as the previous one.</p></div>
<div class="paragraph"><p>Context managers have very appealing applications in testing, where
there may be many test cases that each have lots of setup and teardown.
It’s usually important that individual tests are isolated, so that running
one test does not impact the results of a future test; having a teardown
phase that runs reliably even if the test case went badly wrong is very
appealing. Since Python 2.7 (and thus all Python 3 versions), context
managers are composable - that is you can have combinations of multiple
setup and teardown steps, which can even feed into each other, like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>with <span style="font-weight: bold"><span style="color: #000000">a</span></span><span style="color: #990000">(</span>x<span style="color: #990000">,</span> y<span style="color: #990000">)</span> as A<span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #000000">b</span></span><span style="color: #990000">(</span>A<span style="color: #990000">)</span> as C<span style="color: #990000">:</span></tt></pre></div></div>
<div class="paragraph"><p>Hopefully this post will have shown some of the uses of the "with"
statement. As always, there are more goodies, only need to do a little
more digging!</p></div>
</div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2017-01-27 14:35:45 MST
</div>
</div>
</body>
</html>
Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com0tag:blogger.com,1999:blog-5018861242355523355.post-62805042649271942872017-01-08T07:40:00.000-08:002017-01-27T13:57:55.963-08:00What this blog is aboutWrite Better Python? Maybe the title sounds a little pretentious
when there are a number of justifiably well known folks in the Python
community writing and presenting on the same topic. So just a few
words of explanation: at the turn of the century, I was a devoted Python
enthusiast. It was was basically the early days of Python 2. I wrote lots of code, I developed a Python course for a
well known training company, I served as a tech editor for the second
edition of an iconic Python book, and had a draft of my own Python book
in the works. And then I took a very busy job that was not at all
Python-focused, and after about 11 years, a second one. Python for the
odd tool here and there? Sure. But Python was no longer a focus for me,
due to time pressures.<br />
<br />
I taught my last instance of my
Python course in January 2002. 15 years later in January 2017 I've
dug back into the language and I found a lot of things I'd done were pretty
simplistic - maybe not exactly "writing C in Python", but on that kind
of level, not really expressing things in a modern Pythonic way.<br />
<br />
The purpose of the blog is to write about interesting Python aspects as I discover
things that have happened in the decade and a half where I've not been
able to pay very much attention. This is much more about my own voyage of discovery than trying to outdo any of the luminaries. I'll be looking at what it means to be "Pythonic", where some refactoring can make more readable code, when to apply the EAFP (Easier to Ask Forgiveness Principle - yes, it's even been given a name) and more. Even though my main objective is to understand things better by writing them up, I hope this material will prove useful to some others! Anonymoushttp://www.blogger.com/profile/07736425169208292868noreply@blogger.com0