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"> /*<![CDATA[*/ var asciidoc = { // Namespace. ///////////////////////////////////////////////////////////////////// // Table Of Contents generator ///////////////////////////////////////////////////////////////////// /* Author: Mihai Bazon, September 2002 * http://students.infoiasi.ro/~mishoo * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ /* modified by Stuart Rackham, 2006, 2009. License: GPL */ // toclevels = 1..4. toc: function (toclevels) { function getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants. text += i.data; else if (i.firstChild != null) text += getText(i); } return text; } function TocEntry(el, text, toclevel) { this.element = el; this.text = text; this.toclevel = toclevel; } function tocEntries(el, toclevels) { var result = new Array; var re = new RegExp('[hH]([1-'+(toclevels+1)+'])'); // 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: &#8220;I defined a derived class but it is not getting the data from the base class. What&#8217;s going on?&#8221;</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 &lt;module&gt; 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&#8217;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&#8217;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>&lt;1&gt;</b> Setting class variables in B Begin examination... Data from classes: A.clsdataA: class A <b>&lt;2&gt;</b> B.clsdataA: class A Instantiating A as a: Initializing instance of A <b>&lt;3&gt;</b> Data from instance a: a.clsdataA: class A <b>&lt;4&gt;</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>&lt;5&gt;</b> Data from instance b: b.clsdataB: class B b.dataB: instance of class B b.clsdataA: class A <b>&lt;6&gt;</b> call methA from b: A method from class A Traceback (most recent call last): File "./override.py", line 41, in &lt;module&gt; 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&#8217;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&#8217;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&#8217;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>(&lt;class '__main__.B'&gt;, &lt;class '__main__.A'&gt;, &lt;type 'object'&gt;)</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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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">&quot;__main__&quot;</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">&#39;abcd&#39;</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">&#39;abcd&#39;</span><span class="p">)</span> <span class="k">assert</span> <span class="n">output</span> <span class="o">==</span> <span class="s1">&#39;edcba&#39;</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(&#39;abcd&#39;) &gt; assert output == &#39;edcba&#39; E assert &#39;dcba&#39; == &#39;edcba&#39; 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">&#39;abcd&#39;</span><span class="p">,</span> <span class="s1">&#39;dcba&#39;</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"> &copy;2017, Mats Wichmann. | Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.2</a> &amp; <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"> /*<![CDATA[*/ var asciidoc = { // Namespace. ///////////////////////////////////////////////////////////////////// // Table Of Contents generator ///////////////////////////////////////////////////////////////////// /* Author: Mihai Bazon, September 2002 * http://students.infoiasi.ro/~mishoo * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ /* modified by Stuart Rackham, 2006, 2009. License: GPL */ // toclevels = 1..4. toc: function (toclevels) { function getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants. text += i.data; else if (i.firstChild != null) text += getText(i); } return text; } function TocEntry(el, text, toclevel) { this.element = el; this.text = text; this.toclevel = toclevel; } function tocEntries(el, toclevels) { var result = new Array; var re = new RegExp('[hH]([1-'+(toclevels+1)+'])'); // 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">&lt;</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab853b5f48</span><span class="o">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7fab7d3d9818</span><span class="o">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</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">&gt;</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">&lt;</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5818</span><span class="o">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</span><span class="nb">property</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x7f7ba29b5868</span><span class="o">&gt;</span> <span class="o">&lt;</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">&gt;</span> <span class="o">&lt;</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">&gt;</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"> /*<![CDATA[*/ var asciidoc = { // Namespace. ///////////////////////////////////////////////////////////////////// // Table Of Contents generator ///////////////////////////////////////////////////////////////////// /* Author: Mihai Bazon, September 2002 * http://students.infoiasi.ro/~mishoo * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ /* modified by Stuart Rackham, 2006, 2009. License: GPL */ // toclevels = 1..4. toc: function (toclevels) { function getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants. text += i.data; else if (i.firstChild != null) text += getText(i); } return text; } function TocEntry(el, text, toclevel) { this.element = el; this.text = text; this.toclevel = toclevel; } function tocEntries(el, toclevels) { var result = new Array; var re = new RegExp('[hH]([1-'+(toclevels+1)+'])'); // 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>&#8230;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, &#8220;because you don&#8217;t need that in Python&#8221;. I&#8217;d like to take some time to talk about that, because the ideas behind it aren&#8217;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&#8217;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&#8217;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 (&#8220;set things up&#8221;) 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">&gt;&gt;&gt;</span> <span class="n">a</span><span class="o">=</span><span class="mi">2</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">a</span><span class="o">=</span><span class="mi">4</span> <span class="o">&gt;&gt;&gt;</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">&gt;&gt;&gt;</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">&quot;Hello&quot;</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">function</span> <span class="n">a</span> <span class="n">at</span> <span class="mh">0x7f7fa29b4c80</span><span class="o">&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">a</span><span class="p">()</span> <span class="n">Hello</span> <span class="o">&gt;&gt;&gt;</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">&quot;Hi, argument was&quot;</span><span class="p">,</span> <span class="n">x</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">function</span> <span class="n">a</span> <span class="n">at</span> <span class="mh">0x7f7fa29b4cf8</span><span class="o">&gt;</span> <span class="c1"># <b>&lt;1&gt;</b></span> <span class="o">&gt;&gt;&gt;</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">&quot;&lt;stdin&gt;&quot;</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">&lt;</span><span class="n">module</span><span class="o">&gt;</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">&gt;&gt;&gt;</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">&gt;&gt;&gt;</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&#8217;re not really the same name, but they look that way in your source code. That&#8217;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">&quot;text&quot;</span><span class="p">)</span> </pre></div></div></div> <div class="paragraph"><p>That is, we&#8217;ve instantiated with three different types of arguments, but the initializer doesn&#8217;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">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">&gt;&gt;&gt;</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">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mf">18.0</span> <span class="o">&gt;&gt;&gt;</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">&gt;&gt;&gt;</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&#8217;s a concept often cited in Python circles that it&#8217;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&#8217;t work. That&#8217;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&#8217;s not any real problem. That idea isn&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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 /> &#8212; "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&#8217;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&#8217;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>&lt;1&gt;</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>&lt;2&gt;</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>&lt;3&gt;</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&#8217;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&#8217;s keyword argument passing. Rather than trying to put this in words, here&#8217;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">&quot;emp_str&quot;</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">&quot;emp_str&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;-&#39;</span><span class="p">)</span> <span class="k">elif</span> <span class="s2">&quot;first&quot;</span> <span class="ow">in</span> <span class="n">kwargs</span> <span class="ow">and</span> <span class="s2">&quot;last&quot;</span> <span class="ow">in</span> <span class="n">kwargs</span> <span class="ow">and</span> <span class="s2">&quot;pay&quot;</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">&quot;first&quot;</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">&quot;last&quot;</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">&quot;pay&quot;</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">&quot;invalid initializer:&quot;</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">&quot;Name: {} {}, Pay: {}&quot;</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">&quot;John&quot;</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">&quot;Public&quot;</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>&lt;1&gt;</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">&quot;Test-Employee-60000&quot;</span><span class="p">)</span> <span class="c1"># <b>&lt;2&gt;</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">&quot;Employees:&quot;</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">&#39;-&#39;</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">&quot;invalid initializer&quot;</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">&quot;Name: {} {}, Pay: {}&quot;</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">&quot;John&quot;</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">&quot;Public&quot;</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>&lt;1&gt;</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">&quot;Test-Employee-60000&quot;</span><span class="p">)</span> <span class="c1"># <b>&lt;2&gt;</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">&quot;Employees:&quot;</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&#8217;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&#8217;s not something I had learned about in those early Python 2 days.</p></div> <div class="paragraph"><p>To know what&#8217;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">&#39;-&#39;</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">&quot;Name: {} {}, Pay: {}&quot;</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">&quot;John&quot;</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">&quot;Public&quot;</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>&lt;1&gt;</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">&quot;Test-Employee-60000&quot;</span><span class="p">)</span> <span class="c1"># <b>&lt;2&gt;</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&#8217;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">&quot;John&quot;</span><span class="p">,</span> <span class="n">last</span><span class="o">=</span><span class="s2">&quot;Public&quot;</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">&quot;Test-Employee-60000&quot;</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"> /*<![CDATA[*/ var asciidoc = { // Namespace. ///////////////////////////////////////////////////////////////////// // Table Of Contents generator ///////////////////////////////////////////////////////////////////// /* Author: Mihai Bazon, September 2002 * http://students.infoiasi.ro/~mishoo * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ /* modified by Stuart Rackham, 2006, 2009. License: GPL */ // toclevels = 1..4. toc: function (toclevels) { function getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants. text += i.data; else if (i.firstChild != null) text += getText(i); } return text; } function TocEntry(el, text, toclevel) { this.element = el; this.text = text; this.toclevel = toclevel; } function tocEntries(el, toclevels) { var result = new Array; var re = new RegExp('[hH]([1-'+(toclevels+1)+'])'); // 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&#8217;s some resource you need access to, but don&#8217;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&#8217;t tend to scatter all over the place, but it&#8217;s still easy to get into trouble, as an example below will show.</p></div> <div class="paragraph"><p>Here&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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.&nbsp; 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.&nbsp; 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&nbsp; 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