Bitten by my C/Java experience

classic Classic list List threaded Threaded
28 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Cecil Westerhof
Potential dangerous bug introduced by programming in Python as if it
was C/Java. :-(
I used:
    ++tries
that has to be:
    tries += 1

Are there other things I have to be careful on? That does not work as
in C/Java, but is correct syntax.

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Tobiah-3
On 05/04/2015 08:20 AM, Cecil Westerhof wrote:

> Potential dangerous bug introduced by programming in Python as if it
> was C/Java. :-(
> I used:
>      ++tries
> that has to be:
>      tries += 1
>
> Are there other things I have to be careful on? That does not work as
> in C/Java, but is correct syntax.
>

One surprise for the new user is an otherwise handy rule of scope.
A variable in a function will by default access any global variables of
the same name *unless* it is assigned to in the function.

def glob():
         print "global:", foo

def loc():
         foo = 2
         print "local:", foo

def alt():
         global foo
         foo = 1
         print "altered:", foo

foo = 3

glob()
print "Original:", foo

loc()
print "Original:", foo

alt()
print "Original:", foo

################# Output ##################

global: 3
Original: 3
local: 2
Original: 3
altered: 1
Original: 1


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Irmen de Jong
In reply to this post by Cecil Westerhof
On 4-5-2015 17:20, Cecil Westerhof wrote:

> Potential dangerous bug introduced by programming in Python as if it
> was C/Java. :-(
> I used:
>     ++tries
> that has to be:
>     tries += 1
>
> Are there other things I have to be careful on? That does not work as
> in C/Java, but is correct syntax.
>

That is a broad question, but one thing that comes to mind is the current (python 3)
behavior of integer division. It gives the exact result and doesn't truncate to integers:


>>> 5/4
1.25


To be prepared for the future you should probably use python's time machine and enable
this behavior for 2.x as well by from __future__ import division.


Another thing is that functions are first class citizens in Python. Java will give a
compiler error if you forget to call them and leave out the parentheses (I think Java 8
allows it though). Python will accept passing them on as a function object just fine. If
you do this by mistake you will probably get an exception a tiny bit down the line, at
runtime. But it is syntactically correct so your code will compile without error.


-irmen



Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Chris Angelico
On Tue, May 5, 2015 at 3:32 AM, Irmen de Jong <irmen.NOSPAM at xs4all.nl> wrote:
> That is a broad question, but one thing that comes to mind is the current (python 3)
> behavior of integer division. It gives the exact result and doesn't truncate to integers:
>
>
>>>> 5/4
> 1.25

Using the word "exact" around non-integer values can be a little
ambiguous, since floats are often inexact. But yes, int/int -> float,
and yes, it WILL bite C programmers.

ChrisA


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Ian Kelly-2
In reply to this post by Cecil Westerhof
On Mon, May 4, 2015 at 9:20 AM, Cecil Westerhof <Cecil at decebal.nl> wrote:
> Potential dangerous bug introduced by programming in Python as if it
> was C/Java. :-(
> I used:
>     ++tries
> that has to be:
>     tries += 1
>
> Are there other things I have to be careful on? That does not work as
> in C/Java, but is correct syntax.

Some other gotchas that aren't necessarily related to C/Java but can
be surprising nonetheless:

*    () is a zero-element tuple, and (a, b) is a two-element tuple,
but (a) is not a one-element tuple. Tuples are created by commas, not
parentheses, so use (a,) instead.

*    Default function arguments are created at definition time, not at
call time. So if you do something like:

    def foo(a, b=[]):
        b.append(a)
        print(b)

The b list will be the same list on each call and will retain all
changes from previous calls.

*    super() doesn't do what you might expect in multiple inheritance
situations, particularly if you're coming from Java where you never
have to deal with multiple inheritance. It binds to the next class in
the method resolution order, *not* necessarily the immediate
superclass. This also means that the particular class bound to can
vary depending on the specific class of the object.

*    [[None] * 8] * 8 doesn't create a 2-dimensional array of None. It
creates one list containing None 8 times, and then it creates a second
list containing the first list 8 times, *not* a list of 8 distinct
lists.

*    If some_tuple is a tuple containing a list, then some_tuple[0] +=
['foo'] will concatenate the list *but* will also raise a TypeError
when it tries to reassign the list back to the tuple.


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Mark Lawrence
In reply to this post by Cecil Westerhof
On 04/05/2015 16:20, Cecil Westerhof wrote:

> Potential dangerous bug introduced by programming in Python as if it
> was C/Java. :-(
> I used:
>      ++tries
> that has to be:
>      tries += 1
>
> Are there other things I have to be careful on? That does not work as
> in C/Java, but is correct syntax.
>

Not dangerous at all, your test code picks it up.  I'd also guess, but
don't actually know, that one of the various linter tools could be
configured to find this problem.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence



Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Ian Kelly-2
On Mon, May 4, 2015 at 11:59 AM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:

> On 04/05/2015 16:20, Cecil Westerhof wrote:
>>
>> Potential dangerous bug introduced by programming in Python as if it
>> was C/Java. :-(
>> I used:
>>      ++tries
>> that has to be:
>>      tries += 1
>>
>> Are there other things I have to be careful on? That does not work as
>> in C/Java, but is correct syntax.
>>
>
> Not dangerous at all, your test code picks it up.  I'd also guess, but don't
> actually know, that one of the various linter tools could be configured to
> find this problem.

pylint reports it as an error.


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Terry Reedy
In reply to this post by Ian Kelly-2
On 5/4/2015 1:43 PM, Ian Kelly wrote:

> *    () is a zero-element tuple, and (a, b) is a two-element tuple,
> but (a) is not a one-element tuple. Tuples are created by commas, not
> parentheses, so use (a,) instead.

Which means that a, and a,b (or a,b,) are 1 and 2 element tuples
respectively. Except for empty tuples, parentheses are optional unless
needed to fence off the tuple from surrounding code (which happens to be
most of the time, but not always).  A trailing comma is prohibited for
zero element tuples, required for one element tuples, and optional for
multiple element tuples.

--
Terry Jan Reedy



Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Cecil Westerhof
In reply to this post by Mark Lawrence
Op Monday 4 May 2015 21:39 CEST schreef Ian Kelly:

> On Mon, May 4, 2015 at 11:59 AM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:
>> On 04/05/2015 16:20, Cecil Westerhof wrote:
>>>
>>> Potential dangerous bug introduced by programming in Python as if
>>> it was C/Java. :-( I used: ++tries that has to be: tries += 1
>>>
>>> Are there other things I have to be careful on? That does not work
>>> as in C/Java, but is correct syntax.
>>>
>>
>> Not dangerous at all, your test code picks it up. I'd also guess,
>> but don't actually know, that one of the various linter tools could
>> be configured to find this problem.
>
> pylint reports it as an error.

I installed it. Get a lot of messages. Mostly convention. For example:
    Unnecessary parens after 'print' keyword

And:
    Invalid variable name "f"
for:
    with open(real_file, 'r') as f:

But still something to add to my toolbox.

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Andrew Cooper
In reply to this post by Cecil Westerhof
On 04/05/2015 18:43, Ian Kelly wrote:
>
> Some other gotchas that aren't necessarily related to C/Java but can
> be surprising nonetheless:
>
> *    () is a zero-element tuple, and (a, b) is a two-element tuple,
> but (a) is not a one-element tuple. Tuples are created by commas, not
> parentheses, so use (a,) instead.

* {} is an empty set(), not dict().

Particularly subtle when combined with **kwargs

$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo(**kwargs):
...   return { (k, kwargs[k]) for k in kwargs }
...
>>> foo()
set()
>>> foo(a=1)
{('a', 1)}
>>>

~Andrew


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

random832@fastmail.us
On Mon, May 4, 2015, at 16:57, Andrew Cooper wrote:
> * {} is an empty set(), not dict().

You've got it backwards.

> Particularly subtle when combined with **kwargs

The function in your example below _always_ returns a set, and kwargs is
always a dict. There's no subtlety outside of the repr output. The fact
that the empty set (as a result of empty kwargs) repr's as set() is a
consequence of the fact that {} is a dict.

>
> $ python3
> Python 3.4.0 (default, Apr 11 2014, 13:05:11)
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
> >>> def foo(**kwargs):
> ...   return { (k, kwargs[k]) for k in kwargs }
> ...
> >>> foo()
> set()
> >>> foo(a=1)
> {('a', 1)}
> >>>
>
> ~Andrew
> --
> https://mail.python.org/mailman/listinfo/python-list


--
Random832


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

TIm Chase-3
In reply to this post by Andrew Cooper
On 2015-05-04 21:57, Andrew Cooper wrote:

> On 04/05/2015 18:43, Ian Kelly wrote:
> >
> > Some other gotchas that aren't necessarily related to C/Java but
> > can be surprising nonetheless:
> >
> > *    () is a zero-element tuple, and (a, b) is a two-element
> > tuple, but (a) is not a one-element tuple. Tuples are created by
> > commas, not parentheses, so use (a,) instead.
>
> * {} is an empty set(), not dict().
>
> Particularly subtle when combined with **kwargs
>
> $ python3
> Python 3.4.0 (default, Apr 11 2014, 13:05:11)
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more
> information.
> >>> def foo(**kwargs):
> ...   return { (k, kwargs[k]) for k in kwargs }
> ...
> >>> foo()
> set()
> >>> foo(a=1)
> {('a', 1)}
> >>>

It's a dict:

Python 3.2.3 (default, Feb 20 2013, 14:44:27)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> type({})
<class 'dict'>


What you're seeing is that your generator creates single-element
tuples in a set constructor (note that your last item isn't
"{'a': 1}". Try instead

>>> def foo(**kwargs):
...     return {k: kwargs[k] for k in kwargs}
...
>>> foo()
{}
>>> foo(a=42)
{'a': 42}

Note the colons, indicating that it's a dict.  You're using the
dict() syntax:

  dict((k,v) for k,v in some_iter())

-tkc








Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

BartC-3
In reply to this post by Cecil Westerhof
On 04/05/2015 16:20, Cecil Westerhof wrote:
> Potential dangerous bug introduced by programming in Python as if it
> was C/Java. :-(
> I used:
>      ++tries
> that has to be:
>      tries += 1

I think I've come across that. It doesn't mind ++ so people are likely
to be assume that increment works as in other languages.

I guess it just means +(+(a)).

But in that case, what meaning does:

a

or

a+b

have in Python? If they were function calls: a() or (a+b)(), then that's
clear enough. But a+b doesn't do anything!

(I think I would have picked up "++" and "--" as special tokens even if
increment/decrement ops weren't supported. Just because they would
likely cause errors through misunderstanding.)

--
Bartc


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Dave Angel-4
In reply to this post by Cecil Westerhof
On 05/04/2015 04:28 PM, Cecil Westerhof wrote:

> Op Monday 4 May 2015 21:39 CEST schreef Ian Kelly:
>
>> On Mon, May 4, 2015 at 11:59 AM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:
>>> On 04/05/2015 16:20, Cecil Westerhof wrote:
>>>>
>>>> Potential dangerous bug introduced by programming in Python as if
>>>> it was C/Java. :-( I used: ++tries that has to be: tries += 1
>>>>
>>>> Are there other things I have to be careful on? That does not work
>>>> as in C/Java, but is correct syntax.
>>>>
>>>
>>> Not dangerous at all, your test code picks it up. I'd also guess,
>>> but don't actually know, that one of the various linter tools could
>>> be configured to find this problem.
>>
>> pylint reports it as an error.
>
> I installed it. Get a lot of messages. Mostly convention. For example:
>      Unnecessary parens after 'print' keyword

Sounds like it's configured for Python 2.x.  There's probably a setting
to tell it to use Python3 rules.

>
> And:
>      Invalid variable name "f"
> for:
>      with open(real_file, 'r') as f:

Sounds like a bad wording.  Nothing invalid about it, though it is a bit
short.  There are certain one letter variables which are so common as to
be expected, but others should be avoided.


>
> But still something to add to my toolbox.
>


--
DaveA


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Steven D'Aprano-11
In reply to this post by BartC-3
On Tuesday 05 May 2015 08:02, BartC wrote:

> On 04/05/2015 16:20, Cecil Westerhof wrote:
>> Potential dangerous bug introduced by programming in Python as if it
>> was C/Java. :-(
>> I used:
>>      ++tries
>> that has to be:
>>      tries += 1
>
> I think I've come across that. It doesn't mind ++ so people are likely
> to be assume that increment works as in other languages.
>
> I guess it just means +(+(a)).

Correct.


> But in that case, what meaning does:
>
> a
>
> or
>
> a+b
>
> have in Python? If they were function calls: a() or (a+b)(), then that's
> clear enough. But a+b doesn't do anything!

Not so.

The first one just does a name lookup and then throws the result away. The
second one looks up names a and b, then adds them together, throwing away
the result.

Here is one use for the first idiom:

try:
    bin
except NameError:
    # No built-in bin function, perhaps our Python is too old?
    def bin(num):
        ...


Here's a good use for the second:


def calculate(x):
    x + 0  # Fails if x is not a number.
    ...


That's an example of duck-typing. It allows any argument which supports
addition with integers, e.g. x could be a number, or some kind of array or
vector which supports addition with a scalar.


> (I think I would have picked up "++" and "--" as special tokens even if
> increment/decrement ops weren't supported. Just because they would
> likely cause errors through misunderstanding.)

Just because C made a mistake, doesn't mean other languages have to
slavishly follow it.



--
Steven



Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

BartC-3
On 05/05/2015 09:19, Steven D'Aprano wrote:
> On Tuesday 05 May 2015 08:02, BartC wrote:

>> (I think I would have picked up "++" and "--" as special tokens even if
>> increment/decrement ops weren't supported. Just because they would
>> likely cause errors through misunderstanding.)
>
> Just because C made a mistake, doesn't mean other languages have to
> slavishly follow it.

I would have thought there was more rapport between the two languages.
Python is often implemented in C and extensions are often implemented in
C, suggesting there are quite a few people familiar with both, sometimes
in areas that are critical (ie. creating code that will affect thousands
of Python apps).

So why pretend that ++ and -- don't exist? After all Python borrows "=",
"==" and "!=" from C.

(Writing a==b instead of a=b is less likely in Python than in a language
where a=b is an equality test rather than assignment. But I've used just
such a language where mistakenly writing a=b (which happens when
switching between languages) caused difficult-to-find bugs.

Until I disallowed standalone expressions as statements, then these
things are picked up, and they are invariably unintended errors. Where
it is actually necessary to evaluate an expression and throw away the
result, then a simple prefix can be used.)

--
Bartc






Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Rustom Mody
On Tuesday, May 5, 2015 at 6:50:29 PM UTC+5:30, BartC wrote:

> On 05/05/2015 09:19, Steven D'Aprano wrote:
> > On Tuesday 05 May 2015 08:02, BartC wrote:
>
> >> (I think I would have picked up "++" and "--" as special tokens even if
> >> increment/decrement ops weren't supported. Just because they would
> >> likely cause errors through misunderstanding.)
> >
> > Just because C made a mistake, doesn't mean other languages have to
> > slavishly follow it.
>
> I would have thought there was more rapport between the two languages.
> Python is often implemented in C and extensions are often implemented in
> C, suggesting there are quite a few people familiar with both, sometimes
> in areas that are critical (ie. creating code that will affect thousands
> of Python apps).

There are mistakes and there are mistakes.
Like there are crimes and crimes, some get community service, some get death sentence

>
> So why pretend that ++ and -- don't exist? After all Python borrows "=",
> "==" and "!=" from C.
>
> (Writing a==b instead of a=b is less likely in Python than in a language
> where a=b is an equality test rather than assignment. But I've used just
> such a language where mistakenly writing a=b (which happens when
> switching between languages) caused difficult-to-find bugs.

Yeah I happen to me in that minuscule minority that regards '= denotes
assignment' a bigger mistake than ++

Interestingly K&R admitted to the fact that the precedences of C have errors;
in particular x&y == 0x1  groups wrong and so invariably requires brackets.
Unfortunately no such admission for the choice of the most ubiquitous operator =.


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Gregory Ewing
In reply to this post by Steven D'Aprano-11
Steven D'Aprano wrote:

> The first one just does a name lookup and then throws the result away. The
> second one looks up names a and b, then adds them together, throwing away
> the result.

Actually, it's worse than that. :-) It's possible for a to
have an __add__ method with a side effect, although that
would be evil.

It's also possible for merely looking up a name to have
a side effect, if it's done in the right context -- but
that would be even more evil.

--
Greg


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Gregory Ewing
In reply to this post by BartC-3
BartC wrote:

> So why pretend that ++ and -- don't exist?

Probably because Python would gain very little from
having them.

Main uses of ++ in C are things like integer for
loops:

    for (i = 0; i < 10; i++) {...

and stepping through arrays:

    a[i++] = b[j++];

Python code usually operates at a higher level than
that.

--
Greg


Reply | Threaded
Open this post in threaded view
|

Bitten by my C/Java experience

Antoon Pardon
In reply to this post by Rustom Mody
Op 05-05-15 om 18:24 schreef Rustom Mody:

> Yeah I happen to me in that minuscule minority that regards '= denotes
> assignment' a bigger mistake than ++

Nice to know I'm not alone. I Especially think it is a mistake, because
it is then used as a reason for not allowing something like

  if a = b - 1:

arguing it would lead to some difficult bugs.

Which in my mind is arguing backwards. Either you think an assigment
in a condition is useful or harmful. In the first case you then look
for an assignment token or assignment syntax that is not that likely
to lead to difficult to discover bugs instead of letting a possible
misleading token or syntax prevent you from implementing something
useful.

In the second case you just state why you think an assignment in a
condition is harmful. No need to hide behind awkward syntax.

--
Antoon Pardon



12