tag:blogger.com,1999:blog-31293552354124628782024-03-19T01:48:25.508-07:00Python Does What?!?Kind of like "hey guys, check it out you can just duct tape down the dead-man's switch on this power tool and use it one handed". In Python.Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.comBlogger115125tag:blogger.com,1999:blog-3129355235412462878.post-9361473049847128762024-03-14T22:09:00.000-07:002024-03-14T22:09:17.623-07:00Wisdom for the ages<pre><code>>>>type(type) is type
True</code></pre>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com0tag:blogger.com,1999:blog-3129355235412462878.post-76148325992591787272024-03-14T12:45:00.000-07:002024-03-14T12:45:27.552-07:00sequence unpack a dict<pre><code>>>> a, b = {"a": 1, "b": 2}
>>> a
'a'</code></pre>
Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com0tag:blogger.com,1999:blog-3129355235412462878.post-77292060213563388532023-03-06T09:22:00.001-08:002023-03-07T14:14:10.947-08:00Annotation InheritanceLet's talk about <a href="https://peps.python.org/pep-0484/">annotations</a>.
<br><br>
Type annotations in Python are mostly a static declaration to a type-checker
like <a href="https://mypy-lang.org/">mypy</a> or <a href="https://github.com/microsoft/pyright">pyright</a>
about the expected types. However, they are also a dynamic data structure which a
growing number of libraries such as the original <a href="https://www.attrs.org/en/stable/overview.html">attrs</a>
and <a href="https://docs.python.org/3/library/dataclasses.html">dataclasses</a> in the standard library, and
even
<a href="https://docs.sqlalchemy.org/en/20/changelog/whatsnew_20.html#native-support-for-dataclasses-mapped-as-orm-models">sqlalchemy</a>
use at runtime.
<pre><code>>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class C:
... a: int
... b: str
...
>>> C(1, "a")
C(a=1, b='a')</code></pre>
These libraries inspect the annotations of a class to <a href="https://github.com/python/cpython/blob/main/Lib/dataclasses.py#L427">generate</a>
<code>__init__</code> and <code>__eq__</code>, saving
a lot of boilerplate code.
You could call this type of API
<a href="https://docs.python.org/3/library/collections.html#collections.namedtuple">named tuple</a> without the tuple.
(To get meta, the typing module has added
<a href="https://docs.python.org/3/library/typing.html#typing.dataclass_transform"><code>dataclass_transform</code></a>
which libraries can use to properly annotate new class decorators with this API.)
<br><br>
These libraries support inheritance of fields.
<pre><code>>>> @dataclass
... class D(C):
... e: int
...
>>> D(1, "a", 2)
D(a=1, b='a', e=2)</code></pre>
Type checkers also consider class annotations to be inherited.
For example, <code>mypy</code> considers this to be correct:
<pre><code>class A:
a: int
class B(A): pass
B().a</code></pre>
That code fails at runtime, because nothing is actually setting <code>a</code> on
the <code>B</code> instance. But, what if <code>B</code> was a dataclass?
<pre><code>>>> class A:
... a: int
...
>>> @dataclass
... class B:
... pass
...
>>> B(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes 1 positional argument but 2 were given</code></pre>
It doesn't work, because annotations are not inherited.
<pre><code>>>> A.__annotations__
{'a': <class 'int'>}
>>> B.__annotations__
{}</code></pre>
It's up to the library to look up it's <a href="https://docs.python.org/3/library/stdtypes.html#class.__mro__">inheritance tree</a> and decide to include
the annotations of parents or not when generating code. As it happens,
dataclasses has <a href="https://github.com/python/cpython/blob/main/Lib/dataclasses.py#L932">made the design decision</a> to only inherit annotations from
other dataclasses.
<br><br>
As an aside, class variables which are used to represent default values are inherited.
<pre><code>>>> class A:
... a = 1
...
>>> @dataclass
... class B(A):
... a: int
...
>>> B()
B(a=1)
</code></pre>
We can write another decorator which grabs annotations
from parents and adds them in <a href="https://www.python.org/download/releases/2.3/mro/">method resolution order</a>, as if they were inherited.
<pre><code>def inherit_annotations(cls):
annotations = {}
for parent in cls.__mro__[::-1]:
# reverse order so children override parents
annotations.update(getattr(parent, "__annotations__", {}))
# use getattr(): not everything has __annotations__
cls.__annotations__.update(annotations)
return cls</code></pre>
Since all dataclasses sees is the <code>__annotations__</code> dict at runtime,
any modifications made before the class decorator runs will be reflected in the generated fields.
<pre><code>>>> @dataclass
... @inherit_annotations
... class B(A): pass
...
>>> B(1)
B(a=1)</code></pre>
<a href="https://gist.github.com/kurtbrose/fc488adb8011aa9a23f9e75d28f5409c">Here's a robustified version of the function</a>.
<br><br>
I know what you're thinking though: why not just use multiple class decorators? Sure, all but one of the generated <code>__init__</code>s
will be overwritten, but that's fine because they all have the same behavior anyway.
<pre><code>import attr
from dataclasses import dataclass
@dataclass
@attr.define
class DualCitizen:
a: int
@dataclass
class Dataclassified(DualCitizen):
pass
@attr.define
class Attrodofined(DualCitizen):
pass</code></pre>
Looks like perfectly normal class definitions.
<pre><code>>>> DualCitizen(1)
DualCitizen(a=1)
>>> Dataclassified(1)
Dataclassified(a=1)
>>> Attrodofined(1)
Attrodofined(1)</code></pre>
And it works.
<br><br>
So, type-checkers consider annotations to be inherited, but class decorators which use annotations at runtime
only inherit annotations from ancestors with the same decorator. We can work around
this either by multiply decorating the ancestors,
or by pulling annotations from ancestors into <code>__annotations__</code>.
Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com0tag:blogger.com,1999:blog-3129355235412462878.post-60876718767617343682022-09-14T21:07:00.004-07:002022-09-14T21:30:08.583-07:00Mock Everything<p> A <a href="https://en.wikipedia.org/wiki/Mock_object">mock object</a> is meant to simulate any API for the purposes of testing.</p><p>The python standard library includes <a href="https://docs.python.org/3/library/unittest.mock.html#quick-guide">MagicMock</a>.</p><p class="p1" style="font-family: Menlo; font-size: 16px; font-stretch: normal; font-variant-east-asian: normal; font-variant-numeric: normal; line-height: normal; margin: 0px;">
<pre><code>>>> from unittest.mock import MagicMock
>>> mock = MagicMock()
>>> mock.a
<MagicMock name='mock.a' id='281473174436496'>
>>> mock[0]
<MagicMock name='mock.__getitem__()' id='281473165975360'>
>>> mock + 1
<MagicMock name='mock.__add__()' id='281473165479264'></code></pre>
<p>However, there is one place where MagicMock fails.</p>
<pre><code>>>> a, b = mock
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 2, got 0)</code></pre>
<p>The syntax which allows a comma separated series of names on the left to unpack the value on the right is known as <a href="https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences">sequence unpacking</a> in python.</p><p>The reason MockObject is incompatible with sequence unpacking is due to a limitation of operator overloading in python when it comes to this piece of syntax. Let's take a look at how the failing line compiles:</p>
<pre><code>>>> import dis
>>> dis.dis(compile("a, b = fake", "string", "exec"))
1 0 LOAD_NAME 0 (fake)
2 UNPACK_SEQUENCE 2
4 STORE_NAME 1 (a)
6 STORE_NAME 2 (b)
8 LOAD_CONST </code></pre>
<p>We pull fake onto the stack, run the opcode UNPACK_SEQUENCE with a parameter of 2, then store the results into a and b. The issue is that MockObject.__iter__() has no way of knowing that UNPACK_SEQUENCE is expecting two values.</p><p>So, let's cheat and figure out how to do it anyway.</p>
<pre><code>>>> import sys
>>> class MagicSequence:
... def __iter__(self):
... # get the python stack frame which is calling this one
... frame = sys._getframe(1)
... # which instruction index is that frame on
... opcode_idx = frame.f_lasti
... # what instruction does that index translate to
... opcode = frame.f_code.co_code[opcode_idx]
... # is it a sequence unpack instruction?
... if opcode == 92: # opcode.opmap['UNPACK_SEQUENCE']
... # the next byte after the opcode is its parameter,
... # which is the length that the sequence unpack expects
... opcode_param = frame.f_code.co_code[opcode_idx + 1]
... # return an iterator of the expected length
... return iter(range(opcode_param))
... return iter([]) # otherwise, return an empty iterator
...
>>> a, b = MagicSequence()
>>> a, b
(0, 1)
>>> a, b, c = MagicSequence()
>>> a, b, c
(0, 1, 2)</code></pre>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com0tag:blogger.com,1999:blog-3129355235412462878.post-32984370999033571432022-01-16T00:34:00.000-08:002022-01-16T00:34:01.903-08:00Are they equal?<p><span style="font-family: courier;">>>> a = []; a.append(a); a<br />[[...]]<br />>>> b = []; b.append(b); b<br />[[...]]<br />>>> a == b<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />RecursionError: maximum recursion depth exceeded in comparison</span></p>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com7tag:blogger.com,1999:blog-3129355235412462878.post-1647815065081841362020-09-03T17:15:00.000-07:002020-09-03T17:15:35.104-07:00Not counting zeros<p>We all have our favorite way of intentionally raising an exception in Python. Some like referencing an undefined variable to get a simple <span style="font-family: Roboto Mono;">NameError</span>, others might import a module that doesn't exist for a bold <span style="font-family: Roboto Mono;">ImportError</span>.</p><p>But the tasteful exceptioneer knows to reach for that classic computer-confounding conundrum: <b><span style="font-family: Roboto Mono;">1/0</span></b> for a satisfyingly descriptive <span style="font-family: Roboto Mono;">DivisionByZero</span>.</p><p>So, when does dividing by <span style="font-family: Roboto Mono;">0</span> not raise <span style="font-family: Roboto Mono;">DivisionByZero</span>? <br /></p><p>Why, when you divide <span style="font-family: Roboto Mono;">0</span> by a <span style="font-family: Roboto Mono;">Decimal(0)</span>, of course!<br /></p><div style="text-align: left;"><span style="font-size: x-small;"><span style="font-family: Roboto Mono;"><blockquote>>>> from decimal import Decimal<br />>>> Decimal(0) / Decimal(0)<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />decimal.InvalidOperation: [<class 'decimal.DivisionUndefined'>]<br />>>> Decimal(1) / Decimal(0)<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />decimal.DivisionByZero: [<class 'decimal.DivisionByZero'>]</blockquote></span></span></div><p></p><p></p><p></p><p>The numerator type doesn't seem to matter either:<br /><span style="font-family: Roboto Mono;"><span style="font-size: x-small;"></span></span></p><blockquote><span style="font-size: x-small;">>>> 0 / Decimal(0)<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />decimal.InvalidOperation: [<class 'decimal.DivisionUndefined'>]</span></blockquote><p>"<span style="font-family: Roboto Mono;">InvalidOperation</span>" just doesn't quite have the same ring to it! Well, they can't all be heroes. :)<br /></p><p></p>Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com20tag:blogger.com,1999:blog-3129355235412462878.post-27043032020686093712019-09-12T10:45:00.003-07:002020-09-03T17:28:15.204-07:00Welcome to the float zone...<span style="font-family: inherit;">Consider a REPL with tw</span>o tuples, a and b.<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">>>> type(a), type(b)<br />(<type 'tuple'>, <type 'tuple'>)<br />>>> a == b<br />True</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-family: inherit;"></span><br /></span>
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-family: inherit;">So far, so good. But let's dig deeper...</span></span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">>>> a[0] == b[0]<br />False</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">The tuples are equal, but their contents is not. </span><br />
<span style="font-family: "Courier New", Courier, monospace;"></span><br />
<span style="font-family: "Courier New", Courier, monospace;"></span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br />>>> a is b<br />True</span><br />
<span style="font-family: "Courier New", Courier, monospace;"></span><br />
<span style="font-family: "Courier New", Courier, monospace;"></span><br />
<span style="font-family: "Courier New", Courier, monospace;"></span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">In fact, there was only ever one tuple.</span><br />
<span style="font-family: "Courier New", Courier, monospace;">What is this madness?</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">>>> a<br />(nan,)</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">Welcome to the float zone.</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">Many parts of python assume that a is b implies a == b, but floats break this assumption. They also break the assumption that hash(a) == hash(b) implies a == b.</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br /></span>
<span style="font-family: "Courier New", Courier, monospace;">>>> hash(float('nan')) == hash(float('nan'))<br />True</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br /></span>
<span style="font-family: "Courier New", Courier, monospace;">Dicts handle this pretty elegantly:</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br /></span>
<span style="font-family: "Courier New", Courier, monospace;">>>> n = float('nan')<br />>>> {n: 1}[n]<br />1</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br /></span>
<span style="font-family: "Courier New", Courier, monospace;">>>> a = {float('nan'): 1, float('nan'): 2}<br />>>> a<br />{nan: 1, nan: 2}</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com28tag:blogger.com,1999:blog-3129355235412462878.post-64287679496157442102019-06-03T17:12:00.001-07:002019-06-03T17:12:28.670-07:00They say a python tuple can't contain itself...... but here at PDW we abhor that kind of defeatism!<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">>>> import ctypes<br />>>> tup = (None,)<br />>>> ctypes.pythonapi.PyTuple_SetItem.argtypes = ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p<br />>>> ctypes.pythonapi.PyTuple_SetItem(id(tup), 0, id(tup))<br />0</span><br />
<br />
Showing the tuple itself is a little problematic<br />
<span style="font-family: "Courier New", Courier, monospace;">>>> tup</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># ... hundreds of lines of parens ... </span><br />
<span style="font-family: "Courier New", Courier, monospace;">(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((</span><br />
<span style="font-family: "Courier New", Courier, monospace;">(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((</span><br />
<span style="font-family: "Courier New", Courier, monospace;">(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((</span><br />
<span style="font-family: "Courier New", Courier, monospace;">(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((</span><br />
<span style="font-family: "Courier New", Courier, monospace;">(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((</span><br />
<span style="font-family: "Courier New", Courier, monospace;">((Segmentation fault</span><br />
<br />Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com16tag:blogger.com,1999:blog-3129355235412462878.post-6813560286419234932019-01-23T09:00:00.000-08:002019-01-23T09:00:07.688-08:00So a list and a tuple walk into a sum()As a direct side effect of <a href="https://github.com/mahmoud/glom" target="_blank">glom</a>'s <a href="https://github.com/mahmoud/glom/blob/https://github.com/mahmoud/glom/blob/9d18413/CHANGELOG.md#1910/CHANGELOG.md#1910" target="_blank">19.1.0 release</a>, the authors here at PDW got to re-experience one of the more surprising behaviors of three of Python's most basic constructs:<br />
<ul>
<li><span style="font-family: "Courier New", Courier, monospace;"><a href="https://docs.python.org/3/tutorial/datastructures.html#more-on-lists" target="_blank">list()</a></span></li>
<li><span style="font-family: "Courier New", Courier, monospace;"><a href="https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences" target="_blank">tuple()</a></span></li>
<li><span style="font-family: "Courier New", Courier, monospace;"><a href="https://docs.python.org/3/library/functions.html#sum" target="_blank">sum()</a></span></li>
</ul>
Most experienced developers know the quickest way to combine a short list of short lists:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">list_of_lists = [[1], [2], [3, 4]]</span><br />
<span style="font-family: "Courier New", Courier, monospace;">sum(list_of_lists, [])</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># [1, 2, 3, 4]</span></blockquote>
Ah, nice and flat, much better.<br />
<br />
But what happens when we throw a tuple into the mix:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">list_of_seqs = [[1], [2], (3, 4)]</span><br />
<span style="font-family: "Courier New", Courier, monospace;">sum(list_of_seqs, []) </span><br />
<span style="font-family: "Courier New", Courier, monospace;"># TypeError: can only concatenate list (not "tuple") to list</span></blockquote>
This is kind of surprising! Especially when you consider this:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">seq = [1, 2]</span><br />
<span style="font-family: "Courier New", Courier, monospace;">seq += (3, 4)</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># [1, 2, 3, 4]</span></blockquote>
Why should <span style="font-family: "Courier New", Courier, monospace;">sum()</span> fail when addition succeeds?! We'll get to that. <br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">new_list = [1, 2] + (3, 4)</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># TypeError: can only concatenate list (not "tuple") to list </span></blockquote>
There's that error again!<br />
<br />
The trick here is that Python has two addition operators. The simple <a href="https://docs.python.org/3/reference/datamodel.html#object.__add__" target="_blank">"+" or "add"</a> operator, used by sum(), and the more nuanced <a href="https://docs.python.org/3/reference/datamodel.html#object.__iadd__" target="_blank">"+=" or "iadd"</a> operator, add's <a href="https://docs.python.org/3.7/library/operator.html#inplace-operators" target="_blank">inplace variant</a>.<br />
<br />
But why is ok for one addition to error and the other to succeed?<br />
<br />
Symmetry. And maybe <a href="https://en.wikipedia.org/wiki/Commutative_property" target="_blank">commutativity</a> if you remember that math class.<br />
<br />
"+" in Python is symmetric: <span style="font-family: "Courier New", Courier, monospace;">A + B</span> and <span style="font-family: "Courier New", Courier, monospace;">B + A</span> should always yield the same result. To do otherwise would be more surprising than any of the surprises above. list and tuple cannot be added with this operator because in a mixed-type situation, the return type would change based on ordering.<br />
<br />
Meanwhile, "+=" is asymmetric. The left side of the statement determines the type of the return completely. <span style="font-family: "Courier New", Courier, monospace;">A += B</span> keeps <span style="font-family: "Courier New", Courier, monospace;">A</span>'s type. A straightforward, Pythonic reason if there ever was one.<br />
<br />
Going back to the start of our story, by building on <span style="font-family: "Courier New", Courier, monospace;">operator.iadd</span>, <a href="https://glom.readthedocs.io/en/latest/api.html#combining-iterables-with-flatten-and-friends" target="_blank">glom's new flatten() function</a> avoids sum()'s error-raising behavior and works wonders on all manner of nesting iterable.Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com131tag:blogger.com,1999:blog-3129355235412462878.post-5552340096745697372018-09-14T10:21:00.002-07:002018-09-14T10:52:48.009-07:00kids these days think data structures grow on treesArgs and kwargs are great features of Python. There is a measurable (though highly variable) cost of them however:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> timeit.timeit(lambda: (lambda a, b: None)(1, b=2))<br />0.16460260000000204</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> timeit.timeit(lambda: (lambda *a, **kw: None)(1, b=2))<br />0.21245309999999762</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> timeit.timeit(lambda: (lambda *a, **kw: None)(1, b=2)) - timeit.timeit(lambda: (lambda a, b: None)(1, b=2))<br />0.14699769999992895</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Constructing that dict and tuple doesn't happen for free:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">>>> timeit.timeit(lambda: ((1,), {'b': 2})) - timeit.timeit(lambda: None)<br />0.16881599999999253</span><br />
<span style="font-family: inherit;"><span style="font-size: small;"><br /></span></span><span style="font-family: inherit;"><span style="font-family: "Courier New", Courier, monospace; font-size: small;">Specifically, it takes about 1/5,000,000th of a second.</span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: inherit;"></span></span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com74tag:blogger.com,1999:blog-3129355235412462878.post-50640170679228375712018-08-10T12:53:00.001-07:002018-08-10T12:53:08.655-07:00python needs a frozenlist<span style="font-family: "Courier New", Courier, monospace;">>>> set() == frozenset()<br />True<br />>>> [] == ()<br />False</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com26tag:blogger.com,1999:blog-3129355235412462878.post-35200941864726378052018-06-05T08:56:00.001-07:002018-06-05T11:30:14.876-07:00when no-ops attack VII: assignment's revengeLet's define a very simple class: <br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> class F(object):<br />... @staticmethod<br />... def f(): return "I'm such a simple function, nothing could go wrong"<br />...</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> F.f()<br />"I'm such a simple function, nothing could go wrong"</span><br />
<br />
Now, let's do a trivial no-op to this class:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> F.f = F.f</span><br />
<br />
Surely nothing changed, right?<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> F.f()<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />TypeError: unbound method f() must be called with F instance as first argument (got nothing instead)</span><br />
<br />
What happened? staticmethod uses the <a href="https://docs.python.org/3/howto/descriptor.html#id3">descriptor protocol</a> in order to return something other than itself when accessed as an attribute. The assignment above is not a no-op, because it is not setting the value back to what it already was, but to what was returned by __get__ of the staticmethod object.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> class F(object):<br />... @staticmethod<br />... def f(): return "I'm not what I seem"<br />...<br />>>> F.f</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><function f at 0x7f05eda596e0></span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> </span><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "courier new" , "courier" , monospace;">F.__dict__['f']</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><staticmethod object at 0x7f05eda5ce50></span><br />
<br />
Version note -- Python3 doesn't raise an exception, although the type still changes from staticmethod to function.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> class F:<br />... @staticmethod<br />... def f(): return "I'm protected by python3 wizardry"<br />...<br />>>> F.f()<br />"I'm protected by python3 wizardry"<br />>>> F.__dict__['f']<br /><staticmethod object at 0x7fd087b739b0><br />>>> F.f = F.f<br />>>> F.__dict__['f']<br /><function F.f at 0x7fd087b5cae8><br />>>> F.f()<br />"I'm protected by python3 wizardry"</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com15tag:blogger.com,1999:blog-3129355235412462878.post-3791799517317134532018-05-31T13:59:00.001-07:002018-05-31T13:59:34.570-07:00(i)t(er)able for one<span style="font-family: inherit;">When you expect that a sequence will only have one item, and are only interested in the first it is common to grab the zeroth element. This will fail if the sequence is unexpectedly empty, but you might unintentionally silently throw away extra elements:</span><br />
<br />
<span style="font-family: inherit;"><span style="font-family: "Courier New", Courier, monospace;">>>> a = 'a'[0]<br />>>> a = ''[0]<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />IndexError: string index out of range<br />>>> a = 'ab'[0] # oops, silently dropped b </span></span><br />
<br />
<span style="font-family: inherit;">An alternative idiom is to use sequence unpacking with a single item. This way neither unexpected condition will silently pass.</span><br />
<span style="font-family: inherit;"> </span><br />
<span style="font-family: "Courier New", Courier, monospace;">>>> a, = 'a'<br />>>> a, = ''<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />ValueError: need more than 0 values to unpack<br />>>> a, = 'ab'<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />ValueError: too many values to unpack</span><br />
Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com7tag:blogger.com,1999:blog-3129355235412462878.post-5112988383296804072018-05-12T19:05:00.001-07:002018-05-12T19:05:26.200-07:00Captain, the python grammar can't take anymore!The expressions are going to tear themselves to pieces!<br />
<span style="font-family: Courier New, Courier, monospace;">>>> 'a' .strip ( ) [ 0 ]</span><br />
<span style="font-family: Courier New, Courier, monospace;">'a'</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com5tag:blogger.com,1999:blog-3129355235412462878.post-34656969509287883062018-04-20T15:53:00.000-07:002018-04-20T15:54:47.056-07:00DISappearing and<a href="https://docs.python.org/3/reference/datamodel.html#basic-customization">Python has a very rich set of operators that can be overloaded.</a> From <span style="font-family: "courier new" , "courier" , monospace;">__get__ </span>to <span style="font-family: "courier new" , "courier" , monospace;">__getattr__</span>, <span style="font-family: "courier new" , "courier" , monospace;">__repr__ </span>to <span style="font-family: "courier new" , "courier" , monospace;">__format__</span>, and <span style="font-family: "courier new" , "courier" , monospace;">__complex__ </span>to <span style="font-family: "courier new" , "courier" , monospace;">__iadd__ </span>you can modify almost every behavior of your type. Conspicuously absent however, are the boolean operators.<br />
<br />
This is why <a href="https://docs.djangoproject.com/en/1.7/ref/models/queries/#q-objects">Django ORM</a> and <a href="https://stackoverflow.com/a/14185275">SQLAlchemy</a> use bitwise & and | to represent SQL and / or.<br />
<br />
Let's take a closer look at how the Python compiler treats these operators:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> import dis</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> dis.dis(lambda: a & b)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 1 0 LOAD_GLOBAL 0 (a)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 3 LOAD_GLOBAL 1 (b)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 6 BINARY_AND</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 7 RETURN_VALUE</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> dis.dis(lambda: a and b)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 1 0 LOAD_GLOBAL 0 (a)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 3 JUMP_IF_FALSE_OR_POP 9</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 6 LOAD_GLOBAL 1 (b)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> >> 9 RETURN_VALUE</span><br />
<br />
Not only can you not override the <span style="font-family: "courier new" , "courier" , monospace;">and </span>operator, the Python VM doesn't even have an opcode for it.<br />
<br />
In return, Python gives you the semantics that <span style="font-family: "courier new" , "courier" , monospace;">a or b</span><span style="font-family: inherit;"> returns not </span><span style="font-family: "courier new" , "courier" , monospace;">True</span><span style="font-family: inherit;"> or </span><span style="font-family: "courier new" , "courier" , monospace;">False</span><span style="font-family: inherit;">, but either </span><span style="font-family: "courier new" , "courier" , monospace;">a </span><span style="font-family: inherit;">or </span><span style="font-family: "courier new" , "courier" , monospace;">b </span><span style="font-family: inherit;">(or </span><span style="font-family: "courier new" , "courier" , monospace;">False </span><span style="font-family: inherit;">if neither is truthy).</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><br /></span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com30tag:blogger.com,1999:blog-3129355235412462878.post-47101043982198384362018-03-22T11:29:00.000-07:002018-03-22T11:29:02.895-07:00The Zen of Empty Lists<i>"There should be one-- and preferably only one --obvious way to do it"</i>. One of the many philosophies that has earned Python its acclaim.<br />
<br />
But while the <a href="https://www.python.org/dev/peps/pep-0020/">Zen of Python</a> limits on the number of obvious ways, the Zen of Python says nothing about the boundless freedom of <b>unobvious</b> ways.<br />
<br />
Let's empty a list named <span style="font-family: "Courier New", Courier, monospace;">bucket</span>.<br />
<br />
The most obvious way is to simply <i>not</i>. 99 times out of 100, you want to assign a new empty list rather than mutating the old.<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket = [] </span></blockquote>
But let's say you really wanted to empty it, well the clearest way is clear:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket.clear()</span></blockquote>
But even <a href="https://docs.python.org/2/tutorial/datastructures.html#more-on-lists">the docs</a> say this is equivalent to:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">del bucket[:]</span></blockquote>
I guess that's crossing the obvious line. Of course, it may be more obvious than <a href="http://norvig.com/python-iaq.html">Norvig's "dumbell" operator</a>:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket[:]=[]</span></blockquote>
Actually the slice assignment can take any iterable, so our list can lift plates of many shapes:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket[:]={}</span></blockquote>
If you don't want your list getting ripped and/or cut, maybe keep it warm with Norvig's ski hat:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket *=0</span></blockquote>
The ski hat is of particular interest because it's using a very obvious list feature, much more commonly used than list.clear(). Nobody would bat an eye at:<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket = [0, 1, 2, 3] * 2</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># bucket = [0, 1, 2, 3, 0, 1, 2, 3]</span></blockquote>
If you multiply any list by 0, you make a <i>new</i> list of length 0. Bizarrely, this is actually true of any multiplier less than 0, too.<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">bucket *= -3</span><br />
<span style="font-family: "Courier New", Courier, monospace;"># bucket = []</span></blockquote>
Safe to say we are deep in the territory of the unobvious. Is there a syntax we might meditate on to take us further?Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com8tag:blogger.com,1999:blog-3129355235412462878.post-55063209518949625382018-01-21T10:00:00.000-08:002018-01-21T10:00:19.787-08:00None on the left<html>
<head>
</head>
<body>
A natural default, <code>None</code> is probably the most commonly assigned value in Python. But what happens if you move it to the left side of that equation?<br /><br/>
In Python 2:<br />
<pre><code>>>> None = 2
File "<stdin>", line 1
SyntaxError: cannot assign to None
</code></pre>
This is similar to what happens when you assign to a literal:<br />
<pre><code>>>> 1 = 2
File "<stdin>", line 1
SyntaxError: can't assign to literal
</code></pre>
In Python 3 this walk on the wild side will get you a slightly different error:<br />
<pre><code>>>> None = 1
File "<stdin>", line 1
SyntaxError: can't assign to keyword
</code></pre>
<code>None</code> has graduated from useful snowflake to full-blown keyword!<br />
</body>
</html>Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com3tag:blogger.com,1999:blog-3129355235412462878.post-16191442172341299692017-11-30T10:59:00.002-08:002017-11-30T11:20:01.028-08:00python3 set literals in 3, 2, 1....<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">>>> {1,2}.add(3)<br />>>> {1}.add(2)<br />>>> {}.add(1)<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br />AttributeError: 'dict' object has no attribute 'add'</span><br />
<br />
<span style="font-family: inherit;">why no empty set literal: </span><br />
<a href="https://mail.python.org/pipermail/python-3000/2006-April/001286.html"><span style="font-family: inherit;">https://mail.python.org/pipermail/python-3000/2006-April/001286.html</span></a><br />
<a href="https://mail.python.org/pipermail/python-3000/2006-May/001666.html"><span style="font-family: inherit;">https://mail.python.org/pipermail/python-3000/2006-May/001666.html </span></a>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com1tag:blogger.com,1999:blog-3129355235412462878.post-47837052226525081332017-11-29T17:09:00.002-08:002017-11-29T17:09:45.768-08:00a __main__ by any other __name__<span style="font-family: "courier new" , "courier" , monospace;">$ cat <<EOF > what_is_happening.py<br />if __name__ == "__main__":<br /> import what_is_happening<br />else:<br /> print("what is happening?")<br />EOF<br /><br />$ python what_is_happening.py <br />what is happening?</span><br />
<br />
Ambiguous entrypoints can create a maze of state in your program. In case the above example doesn't seem so bad, lets make it worse.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "courier new" , "courier" , monospace;"><br />$ cat <<EOF > innocent_bystander.py<br />import what_is_happening<br /><br />def func(): raise what_is_happening.TrustFall('catch me!')<br />EOF</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">$ cat <<EOF > what_is_happening.py<br />import innocent_bystander<br /><br />class TrustFall(Exception): pass<br /><br />if __name__ == "__main__":<br /> try:<br /> innocent_bystander.func()<br /> except TrustFall:<br /> print("gotcha!")<br /> except Exception as e:<br /> print('oops, butterfingers!')<br /> print('{} is not {}.... what have I done?'.format(<br /> type(e), TrustFall))<br />EOF<br /><br />$ python what_is_happening.py <br />oops, butterfingers!<br /><class 'what_is_happening.TrustFall'> is not <class '__main__.TrustFall'>.... what have I done?</span><br />
<br />
What happened? This is <a href="http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html#executing-the-main-module-twice">executing the main module twice</a>, a special case of double import.<br />
<br />
One solution is to put import guards in all entrypoint scripts:<br />
<span style="font-family: "Courier New", Courier, monospace;">if __name__ != "__main__":</span><br />
<span style="font-family: "Courier New", Courier, monospace;"> raise ImportError('double import of __main__') </span><br />
<br />
<br />
<br />
<br />
<br />
<br />Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com4tag:blogger.com,1999:blog-3129355235412462878.post-44777661974146544862017-11-29T09:32:00.000-08:002017-11-29T10:14:21.013-08:00UnicodeDecode SyntaxError<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: inherit;">When executing a bytecode for the '+' operation, an invalid byte will raise UnicodeDecodeError. However, when concatenating adjacent string and unicode constants, it will be a SyntaxError. (I guess because there is not byte-code executing this is happening at compile time.)</span> </span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> u'a' + '\xff'<br />Traceback (most recent call last):<br /> File "<stdin>", line 1, in <module><br /><span style="background-color: yellow;">UnicodeDecodeError</span>: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br />>>> u'a' '\xff'<br /> File "<stdin>", line 1<br /><span style="background-color: yellow;">SyntaxError</span>: (unicode error) 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com7tag:blogger.com,1999:blog-3129355235412462878.post-70913013441501381222017-08-24T17:29:00.000-07:002017-08-24T17:31:30.254-07:00sqlite does what<span style="font-family: "courier new" , "courier" , monospace;">>>> import sqlite3</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> c = sqlite3.connect(':memory:')</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><sqlite3.Connection object at 0x10d25c9d0></span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> c.execute('select null and 1').fetchall()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">[(None,)]</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> c.execute('select null and 0').fetchall()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">[(0,)]</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> c.execute('select null or 1').fetchall()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">[(1,)]</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> c.execute('select null or 0').fetchall()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">[(None,)]</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span style="font-family: inherit;">SQlite's docs are fantastic: </span><a href="https://sqlite.org/nulls.html" style="font-family: "courier new", courier, monospace;">https://sqlite.org/nulls.html</a></span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com1tag:blogger.com,1999:blog-3129355235412462878.post-83633384493965038092017-04-29T12:46:00.001-07:002017-04-29T12:56:23.823-07:00a return to yieldI remember when, almost a decade ago, I was first discovering generators. It was a heady time, and I saw applications <a href="https://github.com/mahmoud/euler">everywhere</a>.<br />
<br />
<pre>def fib_gen():
x, y = 1, 1
while x < 100:
x, y = y, x + y
yield x
return
</pre>
<br />
I also remember the first time I tried to mix a return value into my generator.<br />
<br />
<pre>def fib_gen():
x, y = 1, 1
while x < 100:
x, y = y, x + y
yield x
return True</pre>
<br />
Imagine my surprise, as I'm sure countless others experienced as well: <br />
<br />
<pre>SyntaxError: 'return' with argument inside generator</pre>
A rare compile-time error! Only the decorative, bare <span style="font-family: "courier new" , "courier" , monospace;">return</span> is allowed in generators, where they serve to raise <a href="https://docs.python.org/2/library/exceptions.html#exceptions.StopIteration"><span style="font-family: "courier new" , "courier" , monospace;">StopIteration</span></a>.<br />
<br />
Now, imagine my surprise, so many years later when I import that same code in Python 3.<br />
<br />
<br />
...<br />
<br />
Nothing! No error. So what happened?<br />
<br />
Turns out that <a href="https://docs.python.org/3/library/asyncio-task.html">the coroutine and asyncio machinery</a> of Python 3 has repurposed this old impossibility. <br />
<br />
If we manually iterate to skip over our <span style="font-family: "courier new" , "courier" , monospace;">yield<span style="font-family: inherit;"></span></span>: <br />
<br />
<pre>fib_iter = fib_gen()
for i in range(11):
next(fib_iter)
next(fib_iter)
</pre>
<br />
We see what's really happening with our <span style="font-family: "courier new" , "courier" , monospace;">return</span>:<br />
<br />
<pre>Traceback (most recent call last):
File "fib_gen.py", line 13, in <module>
next(fib_iter)
StopIteration: True
</pre>
<br />
That's right, <span style="font-family: "courier new" , "courier" , monospace;">returns</span> in generators now raise StopIteration with a single argument of the return value.<br />
<br />
Most of the time you won't see this. StopIterations are automatically consumed and handled correctly by for loops, list comprehensions, and sequence constructors (like list). But it's yet another reason to be extra careful when writing your own generators, specific to Python 3.Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com2tag:blogger.com,1999:blog-3129355235412462878.post-18490397155267913002017-04-12T09:00:00.000-07:002017-04-12T09:00:09.107-07:00Bit by bit: CPU architectureThere are a variety of reasons you might want to know how many bits the architecture of the CPU running your Python program has. Maybe you're about to use some statically-compiled C, or maybe you're just taking a survey.<br />
<br />
Either way, you've got to know. One historical way way is:<br />
<blockquote class="tr_bq">
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">import sys</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">IS_64BIT = sys.maxint > 2 ** 32</span></span></blockquote>
<br />
Except that <a href="https://docs.python.org/2/library/sys.html#sys.maxint">sys.maxint</a> is specific to Python 2. Being the crossover point where ints transparently become longs, sys.maxint doesn't apply in Python 3, where ints and longs have been merged into just one type: <a href="https://docs.python.org/3/library/functions.html#int">int</a> (even though the C calls it <a href="https://docs.python.org/3/c-api/long.html">PyLongObject</a>). And Python 3's introduction of <a href="https://docs.python.org/3/library/sys.html#sys.maxsize">sys.maxsize</a> doesn't help much if you're trying to support Python <2.7, where it doesn't exist.<br />
<br />
So instead we can use the struct module:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<blockquote class="tr_bq">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">import struct</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><span class="pl-c1">IS_64BIT</span> <span class="pl-k">=</span> struct.calcsize(<span class="pl-s"><span class="pl-pds">"</span>P<span class="pl-pds">"</span></span>) <span class="pl-k">></span> <span class="pl-c1">4</span></span></span></blockquote>
<br />
<span class="pl-c1">This is a little less clear, but being backwards and forwards compatible, and given <a href="https://docs.python.org/2/library/struct.html">struct</a> is still part of the standard library, it's a pretty good approach, and is the one taken in <a href="http://boltons.readthedocs.io/en/latest/ecoutils.html">boltons.ecoutils</a>.</span><br />
<span class="pl-c1"><br /></span>
<span class="pl-c1">But let's say you really wanted to get it down to a single line, and even standard library imports were out of the question, for some reason. You could do something like this:</span><br />
<span style="font-size: x-small;"><span class="pl-c1"><br /></span></span>
<blockquote class="tr_bq">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><span class="pl-c1">IS_64BIT = tuple.__itemsize__ > 4</span></span></span></blockquote>
<span class="pl-c1"><br /></span>
<span class="pl-c1">While not extensively documented, a lot of built-in types have __itemsize__ and __basicsize__ attributes, which describes the memory requirement of the underlying structure. For tuples, each item requires a pointer. Pointer size * 8 = bits in the architecture. 4 * 8 = 32-bit architecture, and 8 * 8 = 64-bit architecture.</span><br />
<span class="pl-c1"><br /></span>
<span class="pl-c1">Even though documentation isn't great, the __itemsize__ approach works back to at least Python 2.6 and forward to Python 3.7. Memory profilers like <a href="https://pythonhosted.org/Pympler/asizeof.html">pympler</a> use __itemsize__ and it might work for you, too!</span>Mahmoud Hashemihttp://www.blogger.com/profile/00508211997310717378noreply@blogger.com8tag:blogger.com,1999:blog-3129355235412462878.post-40461128697124116672017-03-21T11:37:00.001-07:002017-03-21T11:46:17.393-07:00When you can update locals()<span style="font-family: "arial" , "helvetica" , sans-serif;">There are two built-in functions, globals and locals. These return dicts of the contents of the global and local scope.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Locals usually refers to the contents of a function, in which case it is a one-time copy. Updates to the dict do not change the local scope:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">>>> def local_fail():</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... a = 1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... locals()['a'] = 2</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... print 'a is', a</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> local_fail()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">a is 1</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">However, in the body of a class definition, locals points to the __dict__ of the class, which is mutable.</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">>>> class Success(object):</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... locals().update({'a': 1})</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">... </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">>>> Success.a</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">1</span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com2tag:blogger.com,1999:blog-3129355235412462878.post-88922118392662949212017-03-13T18:34:00.000-07:002017-03-13T18:34:01.632-07:00identity theft<span style="font-family: "Courier New",Courier,monospace;">>>> class JSON(int):</span><br />
<span style="font-family: "Courier New",Courier,monospace;">... from json import *</span><br />
<span style="font-family: "Courier New",Courier,monospace;">... </span><br />
<span style="font-family: "Courier New",Courier,monospace;">>>> json = JSON()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">>>> json.dumps()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">'0' </span>Kurt Rosehttp://www.blogger.com/profile/11161604109056835253noreply@blogger.com2