Live data from Hacker News

Common Python Mistakes

toptal.com

121–130 of 146 posts

Re: Common Python Mistakes

#121
post #94

Earlier quoted context omitted.

>really it's the behaviour of the two cases taken together that can seem inconsistent. Why do you think so? I think that both cases seem consistent, or rather, correct (and therefore this example should not be treated as a common Python mistake), because x is not assigned a value anywhere in class C, and C inherits from A, so it should be clear to anyone knowing OOP and inheritance, that C's x is the same as A's x. (…

What's happening here is that a variable is inheriting its value from the superclass, except for when it doesn't. And when it doesn't, why is that? Well presumably it's because something's been overridden - OO tells us that's how we change the properties that are inherited from the superclass. No wait, that's not it; nothing's been overridden here. All that's happened is we've assigned a value to B.x, and doing so se…

This is actually the same thing as regular Python scoping rules; there's not even any fancy OOP logic behind it. Here's the same thing, but using global scope and functions instead of classes and inheritance.

     >>> x = 1
     >>> def a():
    ...:     print(x)
    ...:
     >>> def b():
    ...:     x = 2
    ...:     print(x)
    ...:
     >>> def c():
    ...:     print(x)
    ...:
     >>> a(), b(), c()
    1
    2
    1
     >>> x = 3
     >>> a(), b(), c()
    3
    2
    3
I think there is an argument to be made that classes are special and "reaching upwards" into the superclass scope should not occur - a unique copy should be made - but I also think that Python's way of doing it makes enough sense that it is not confusing. The Python devs are at least consistent about having their own way of doing things.

Re: Common Python Mistakes

#123

For the first gotcha, using None as a default argument solves the problem, but checking `if not bar` instead of `if bar is None` can produce different results if bar evaluates to None in a boolean context. >>> def foo(bar=None): ... if not bar: ... bar = [] ... bar.append("baz") ... return bar ... >>> bar = [] >>> foo(bar) ["baz"] >>> bar []

True, but foo shouldn't be modifying bar anyway; make a copy instead:

  bar = list(bar) if bar else []

Re: Common Python Mistakes

#124

Earlier quoted context omitted.

What's happening here is that a variable is inheriting its value from the superclass, except for when it doesn't. And when it doesn't, why is that? Well presumably it's because something's been overridden - OO tells us that's how we change the properties that are inherited from the superclass. No wait, that's not it; nothing's been overridden here. All that's happened is we've assigned a value to B.x, and doing so se…

If instead of x being an integer, it were a function x(), then it makes more sense. Really, for Python, there is no difference between the two in this example. When you assign a new value to B.x, you're overriding the value that B inherited from A. When you override the value in A, any subclass that doesn't have it's own overridden value will use the new A.x, but any subclass that is overridden will be unchanged.

I agree the behaviour makes sense for functions.

I suppose I just prefer the idea that the meaning of assigning a value to a variable should be "assign this value to the variable", rather than "alter the inheritance behaviour of my class such that mutable state is stored in it where it wasn't stored before, and then assign this value to the variable."

Re: Common Python Mistakes

#125
post #121

Earlier quoted context omitted.

What's happening here is that a variable is inheriting its value from the superclass, except for when it doesn't. And when it doesn't, why is that? Well presumably it's because something's been overridden - OO tells us that's how we change the properties that are inherited from the superclass. No wait, that's not it; nothing's been overridden here. All that's happened is we've assigned a value to B.x, and doing so se…

This is actually the same thing as regular Python scoping rules; there's not even any fancy OOP logic behind it. Here's the same thing, but using global scope and functions instead of classes and inheritance. >>> x = 1 >>> def a(): ...: print(x) ...: >>> def b(): ...: x = 2 ...: print(x) ...: >>> def c(): ...: print(x) ...: >>> a(), b(), c() 1 2 1 >>> x = 3 >>> a(), b(), c() 3 2 3 I think there is an argument to be m…

That's an interesting point, that the behaviour of an inherited class variable is consistent with a case you show where inheritance plays no part at all.

So from that point of view, it comes down to whether we expect that an inherited class variable really is just some variable in an outer scope that we can shadow with a local variable of the same name (per your example), or whether we expect that inheritance provides some stronger notion of ownership of the inherited variable.

I dislike the former case, largely because I dislike the idea that the location at which a variable is stored can appear to change merely by assigning to it. But then, I dislike Python's implicit declaration of local variables for exactly the same reason. So you're right, there IS some consistency there. ;-)

Re: Common Python Mistakes

#126
post #113

Does someone knows if the first mistake is also present in Ruby? I ran into this code: def get_analytics_data(options = {}) options = options.merge({'ids' => GAReadonly.configuration.id }) and I wonder if I should fix it.

No need. You can even refer to earlier arguments in the list like

    def foo a, b=a.succ
      b
    end

    foo(1)
    => 
    2

Re: Common Python Mistakes

#128
post #49

Slightly off topic, but does anyone know of a resource that has 'most common mistakes' for different languages all in one place? It's certainly possible to google for blog posts and stack overflow questions to assemble such a list, but it would be handy to have them all in one place. My use case is when interviewing candidates I often ask them to rate themselves on a scale of 1-5 in the languages they know, and then…

> I've found little correlation between how harshly someone rates themselves and their true ability

Are you sure you're actually measuring their true ability by asking them about specific, increasingly obscure gotchas? People can even forget some of the gotchas, even if they've encountered them.

Re: Common Python Mistakes

#129

Earlier quoted context omitted.

If instead of x being an integer, it were a function x(), then it makes more sense. Really, for Python, there is no difference between the two in this example. When you assign a new value to B.x, you're overriding the value that B inherited from A. When you override the value in A, any subclass that doesn't have it's own overridden value will use the new A.x, but any subclass that is overridden will be unchanged.

I agree the behaviour makes sense for functions. I suppose I just prefer the idea that the meaning of assigning a value to a variable should be "assign this value to the variable", rather than "alter the inheritance behaviour of my class such that mutable state is stored in it where it wasn't stored before, and then assign this value to the variable."

x isn't a variable, it's a tag. Because Python is dynamically typed, x can be an int one minute and a function the next, so every attribute on a class is stored as a pointer to an object, including attributes that are integers (which is an object) and functions (which are also objects). Because you aren't declaring the type of x (Python doesn't allow that), Python has to treat x = 1 the same way it'd treat def x(self).

Re: Common Python Mistakes

#130

Earlier quoted context omitted.

I agree the behaviour makes sense for functions. I suppose I just prefer the idea that the meaning of assigning a value to a variable should be "assign this value to the variable", rather than "alter the inheritance behaviour of my class such that mutable state is stored in it where it wasn't stored before, and then assign this value to the variable."

x isn't a variable, it's a tag. Because Python is dynamically typed, x can be an int one minute and a function the next, so every attribute on a class is stored as a pointer to an object, including attributes that are integers (which is an object) and functions (which are also objects). Because you aren't declaring the type of x (Python doesn't allow that), Python has to treat x = 1 the same way it'd treat def x(self…

Where x is an identifier (presumably aka tag?) that refers to a variable, it is not beyond the bounds of possibility for "x = 1" to be interpreted as "store the value of 1 into the variable that is referred to by identifier x". Plenty of languages, including dynamically typed ones, manage to do this, as indeed Python does in many cases.

Not knowing the type of x is unrelated to question of where x's value is stored, or whether x's value will be stored somewhere else after we've assigned a new value to it.

Post reply on HN