Python quirks
lshift.net
lshift.net
def fn(x, my_dict={}):
my_dict[x] = x * 2
return my_dict
>>> fn(1)
{1: 2}
>>> fn(2)
{1: 2, 2: 4}
(I would have expected the second call to return {2: 4} when I was learning python)Default values for functions in Python are instantiated when the function is defined, not when it’s called.
Thread: https://news.ycombinator.com/item?id=5999772
Direct link: http://blog.amir.rachum.com/post/54770419679/python-common-n...
It looks like the author posted a "part 2" to HN as well, but it never made the front page.
http://blog.amir.rachum.com/post/55024295793/python-common-n...
Whereas if you did this instead:
def fn(x):
my_dict={}
my_dict[x] = x * 2
return my_dict
You'd get what you'd expect--a new my_dict object gets created, returned, and then goes out of scope every time fn is called, so it would get garbage collected once there are no more references to that return value. (I think...)(I don't know that much about how memory allocation and GC works in Python yet, just trying to learn!)
def fn(x, my_dict=None):
if my_dict is None:
my_dict = {}
... def a():
print "a called"
return []
def fn(x=a()):
x.append(1)
print x
fn()
fn()
fn()
i suggest typing this directly into the interpreter instead of a script for better effect.While lisp suffers from the same "definition is execution" gotcha, the effects are far rarer in practice because () is immutable and interned while [] is mutable and usually generated afresh each time it's executed.
$ python
>>> [] is []
False
$ sbcl
* (eq () ())
t
Since [] is mutable, appends of [] can do superficially 'the right thing'. >>> a = []
>>> a.append(4)
>>> a
[4]
* (setq a ())
* (nconc a '(34))
(34)
* a
()
But there's a reason lisp does seemingly the wrong thing. According to the spec, nconc skips empty arguments (http://www.lispworks.com/documentation/HyperSpec/Body/f_ncon...) Reading between the lines, I'm assuming this makes sense from a perspective in lisp where we communicate even with 'destructive' operations through their return value. This is more apparent when you consider nreverse: * (setq a '(1 2 3 4))
* (nreverse a)
(4 3 2 1)
* a
(1)
Destructive operations can reuse their input, but they're not required to maintain bindings. There is no precise equivalent of python's .reverse(). Instead, a common idiom is: * (setq a (nreverse a))
It seems like a weird design decision, but one upshot of it besides encouraging a more functional style is that this optional-arg gotcha loses a lot of its power in lisps. It's very rare to define a default param of a non-empty list, and empty lists can't be modified without assigning to them.The "def" statement may construct different distinct function objects from a single definition. Each distinct function object has different default value objects, but each function object reuses its own default value objects each time it's called.
Consider this:
def outer_fn():
def inner_fn(foo={}):
# The id() function returns an internal
# object identifier. Different objects
# have different ids.
print id(foo)
return inner_fn
inner_fn_1 = outer_fn()
inner_fn_2 = outer_fn()
inner_fn_1()
inner_fn_1()
inner_fn_2()
inner_fn_2()To limit its size, you can use the ideas on http://stackoverflow.com/questions/2437617/limiting-the-size...
Confusion is avoided by understanding that comma ',' not paren '()' is the tuple constructor.
> Python doesn’t have multiline comments. Instead, multiline strings are used.
No the are NOT! Don't use strings for comments dammit.
http://www.python.org/dev/peps/pep-0257/#multi-line-docstrin...
You're gonna have to back that up with a reason. I'm tired of pedantry for pedantry's sake.
I'll answer my own question: no.
No offense, but your arrogant "knows it all" attitude is what annoys a lot of people about this community.
1. Read the docs and figure it out yourself.
2. Why are you doing X? Only an intellectually feeble person would do X -- normal people do Y.
3. That's a waste of time and I'm not going to tell you how to do it because X, Y, and Z.
In the provided example he calls get on an empty dictionary for the key 1, then calls getattr of 'a' on an int. Finally he calls it again with an optional default argument of None.
The difference is made apparent by the example:
In [13]: test = {'values': 1}
In [14]: getattr(test, 'values') Out[14]: <function values>
In [15]: test.get('values') Out[15]: 1
>>> True, False = False, True
It doesn't have much practical effect, since most logical tests don't use the True and False constants directly. But it's a good way to perplex the unwary.
if random.randint(0,1): (True,False) = (False,True)
All in-place operations in python are supposed to return None, as a way of explicitly signaling that the operation was done on the existing object. It's not always followed outside the standard library, but it's very rare to run across an exception.
It's counter-intuitive at first, especially in this case, but it is consistent.
There are exceptions (e.g., the "get" prefix, used for getters, which tend to return values and have no side-effects), and, as with any rule of thumb, better to break it than create something ugly. But as rules go, I've found this one a pretty useful one to stick to.
Matz has written about this - https://www.ruby-forum.com/topic/176830#773946 - and said "The bang (!) does not mean "destructive" nor lack of it mean non destructive either. The bang sign means "the bang version is more dangerous than its non bang counterpart; handle with care".
This is one of the most commonly misunderstood things about Ruby in my experience (enough so that some library developers do apply a ! == destructive naming system) and would certainly make an equivalent "Ruby quirks" list IMHO! :-)
>>> [[(a, b) for a in [1, 2, 3]] for b in [4, 5, 6]]
[[(1, 4), (2, 4), (3, 4)], [(1, 5), (2, 5), (3, 5)], [(1, 6), (2, 6), (3, 6)]]
>>> [(a, b) for a in [1, 2, 3] for b in [4, 5, 6]]
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)] for a in [1, 2, 3]:
for b in [4, 5, 6]: for XXXXX:
for YYYYY:
ZZZZZ
it would kind-of make sense to me if this translated into a for comprehension as [ZZZZZ for YYYYY for XXXXX]
but instead it translates to [ZZZZZ for XXXXX for YYYYY]
which seems decidedly middle-endian. class MyContainer(object):
def __getitem__(self, key):
return key
>>> c = MyContainer()
>>> c[1]
1
>>> c[1:]
slice(1, None, None)
>>> c[1:2, 1:2:3, ...]
(slice(1, 2, None), slice(1, 2, 3), Ellipsis)
NumPy uses this to allow advanced slicing of multidimensional arrays:http://docs.scipy.org/doc/numpy/reference/arrays.indexing.ht...
> 999+1 is not 1000
The examples given in this section do not actually show what the poster thinks! Python's interning of small integers is not relevant. It only applies to integers in the range -5 through 256.
The real explanation for why "1000 is 1000" evaluates to True has to do with the Python compiler. By evaluating this expression as a single statement in the interactive interpreter, the compiler notices that the constant value 1000 is repeated more than once. Therefore, it is able to re-use the same object.
But if you provide the value in more than one statement, the compiler is unable to do this:
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
Note that this behavior exists because each statement in the interactive interpreter is compiled separately. If you place the code within a function instead, the entire function is compiled at once, and the object is reused once more: >>> def f():
... a = 257
... b = 257
... return a is b
...
>>> f()
True
> Ellipsis?Apparently Ellipsis is always “bigger” than anything, as opposite to None, which is always “smaller” than anything.
Not so. Ellipsis follows Python 2's default rules for the comparison of unrelated types.
>>> Ellipsis < ()
True
The default is documented as comparing objects "consistently but arbitrarily"[1]. The actual rules are:1) None is the smallest object. 2) Followed by numbers. 3) Followed by all other objects. Objects of distinct types are compared by the lexical ordering of their type names.
This can easily lead to senseless orderings, when two types define an ordered relationship between themselves, but another type happens to have a name that is lexically between them, as with str, tuple, and unicode:
>>> "def" < (1,)
True
>>> (1,) < u"abc"
True
>>> u"abc" < "def"
True
The Ellipsis constant is an instance of a type named "ellipsis", and so it is smaller than instances of most of the other non-numeric builtin types, except for dict.The actual use of Ellipsis has nothing to do with recursive containers printing an ellipsis in their repr. It's part of a wacky special syntax that exists for NumPy's benefit:
>>> d = {}
>>> d[...] = None
>>> d
{Ellipsis: None}
[1] http://docs.python.org/2/reference/expressions.html#not-inGiven
def p(x):
print x
return x
then print 'a', p('b')
is not equivalent to a1 = 'a'
a2 = p('b')
print a1, a2