Noob questions about Python

I

Ixiaus

I have recently (today) just started learning/playing with Python. So
far I am excited and impressed (coming from PHP background).

I have a few questions regarding Python behavior...

val = 'string'
li = list(val)
print li.reverse()

returns nothing, but,

val = 'string'
li = list(val)
li.reverse()
print li

returns what I want. Why does Python do that?

Also I have been playing around with Binary math and noticed that
Python treats:

val = 00110

as the integer 72 instead of returning 00110, why does Python do that?
(and how can I get around it?)

Grateful for any replies!
 
A

Adam Atlas

I have recently (today) just started learning/playing with Python. So
far I am excited and impressed (coming from PHP background).

I have a few questions regarding Python behavior...

val = 'string'
li = list(val)
print li.reverse()

returns nothing, but,

val = 'string'
li = list(val)
li.reverse()
print li

returns what I want. Why does Python do that?

Because list.reverse() modifies a list, it doesn't create and return a
new one.

A common idiom for returning a reversed copy of a list is:
li[::-1]

You can also use the builtin "reversed" -- it doesn't return a list,
but rather a reverse iterator. You can create a list from an iterator
by passing it to the list() initializer, like list(reversed(li)).
 
B

Bruno Desthuilliers

Ixiaus a écrit :
I have recently (today) just started learning/playing with Python. So
far I am excited and impressed

Welcome onboard then !-)
(coming from PHP background).
>
I have a few questions regarding Python behavior...

val = 'string'
li = list(val)
print li.reverse()

returns nothing, but,

val = 'string'
li = list(val)
li.reverse()
print li

returns what I want. Why does Python do that?

list.reverse (like list.sort) is a destructive in-place operation. Not
returning the object is reminder of the destructive nature of the
operation. That's a design choice, whether you agree with it or not
(FWIW, I don't, but I live with it !-)

Note that there's also the reverse() function that returns a reverse
iterator over any sequence, so you could also do:

li = list('allo')
print ''.join(reverse(li))
Also I have been playing around with Binary math and noticed that
Python treats:

val = 00110

as the integer 72 instead of returning 00110, why does Python do that?

Literal integers starting with '0' (zero) are treated as octal. It's a
pretty common convention (like 0x for hexa). FWIW, PHP does just the
same thing.
(and how can I get around it?)

You can't. Python has no literal notation for binary integers so far.
Literal notation for binary ints is not a common feature anyway.

HTH
 
P

Paul Hankin

I have a few questions regarding Python behavior...
as the integer 72 instead of returning 00110, why does Python do that?
(and how can I get around it?)

You can do this:

def bin(x):
return int(x, 2)

val = bin('00110')
 
N

Neil Cerutti

Ixiaus a écrit :

Literal integers starting with '0' (zero) are treated as octal.
It's a pretty common convention (like 0x for hexa). FWIW, PHP
does just the same thing.


You can't. Python has no literal notation for binary integers
so far. Literal notation for binary ints is not a common
feature anyway.

You can obtain a practical workaround using the int built-in
function.
6
 
B

Bjoern Schliessmann

Ixiaus said:
val = 'string'
li = list(val)
print li.reverse()

returns nothing, but,

Yes -- li.reverse() returns None. "print None" prints nothing.
val = 'string'
li = list(val)
li.reverse()
print li

returns what I want.

I'm afraid not. li.reverse() still returns None, but this time you
print li, and this shows the reversed list.

As already explained, li.reverse() modifies the existing list. What
to use here depends on what you want to achieve:

If you really want to reverse the list (i. e. re-sort the list in
memory, and perhaps work with it afterwards), use "li.reverse()".

If you just want to have all its members in reverse order, use the
builtin reversed(li) (which is an iterator giving you li's members
from back to front when iterating over it).

Regards,


Björn
 
I

Ixiaus

Thank you for the quick responses.

I did not know that about integer literals beginning with a '0', so
thank you for the explanation. I never really use PHP except for
handling basic forms and silly web stuff, this is why I picked up
Python because I want to teach myself a more powerful and broad
programming language.

With regard to why I asked: I wanted to learn about Binary math in
conjunction with Python, so I wrote a small function that would return
a base 10 number from a binary number. It is nice to know about the
int() function now.

Just for the sake of it, this was the function I came up with:

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
res.reverse()
print sum(res)

Now that I look at it, I probably don't need that last reverse()
because addition is commutative...

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)

It basically does the same thing int(string, 2) does.

Thank you for the responses!
 
P

Paul Hankin

Thank you for the quick responses.

I did not know that about integer literals beginning with a '0', so
thank you for the explanation. I never really use PHP except for
handling basic forms and silly web stuff, this is why I picked up
Python because I want to teach myself a more powerful and broad
programming language.

With regard to why I asked: I wanted to learn about Binary math in
conjunction with Python, so I wrote a small function that would return
a base 10 number from a binary number. It is nice to know about the
int() function now.

Just for the sake of it, this was the function I came up with:

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
res.reverse()
print sum(res)

Now that I look at it, I probably don't need that last reverse()
because addition is commutative...

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)

Right idea: now to remove all those intermediate lists you construct.
1. reversed(val) creates an iterator that runs over the elements (here
of a string) in reverse order.
2. enumerate() is usually better than using an explicit list index.
3. You can use a generator in your sum to avoid constructing the final
list.

Applying these to your function, and noting that n << k is nicer than
n * 2 ** k, we get a one-liner:

def bin2dec(val):
return sum(int(i) << k for k, i in enumerate(reversed(val)))

Or a slightly nicer alternative is to filter the generator using 'if':

def bin2dec(val):
return sum(1 << k for k, i in enumerate(reversed(val)) if int(i))
 
M

mensanator

Thank you for the quick responses.

I did not know that about integer literals beginning with a '0', so
thank you for the explanation. I never really use PHP except for
handling basic forms and silly web stuff, this is why I picked up
Python because I want to teach myself a more powerful and broad
programming language.

With regard to why I asked: I wanted to learn about Binary math in
conjunction with Python, so I wrote a small function that would return
a base 10 number from a binary number. It is nice to know about the
int() function now.

Just for the sake of it, this was the function I came up with:

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
res.reverse()
print sum(res)

Now that I look at it, I probably don't need that last reverse()
because addition is commutative...

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)

It basically does the same thing int(string, 2) does.

Thank you for the responses!

You could also get ahold of the gmpy module. You get conversion
to binary and also some useful other binary functions as shown
below:

# the Collatz Conjecture in binary

import gmpy

n = 27
print '%4d %s' % (n,gmpy.digits(n,2).zfill(16))

sv = [] # sequence vector, blocks of contiguous LS 0's

while n != 1:
old_n = n
n = 3*n + 1 # result always even
f = gmpy.scan1(n,0) # find least significant 1 bit
n >>= f # remove LS 0's in one fell swoop
sv.append(f) # record f sequence
PopC = gmpy.popcount(n) # count of 1 bits
HamD = gmpy.hamdist(n,old_n) # bits changed
print '%4d %s' % (n,gmpy.digits(n,2).zfill(16)),
print 'PopC:%2d HamD:%2d' % (PopC,HamD)

print sv

## 27 0000000000011011
## 41 0000000000101001 PopC: 3 HamD: 3
## 31 0000000000011111 PopC: 5 HamD: 4
## 47 0000000000101111 PopC: 5 HamD: 2
## 71 0000000001000111 PopC: 4 HamD: 3
## 107 0000000001101011 PopC: 5 HamD: 3
## 161 0000000010100001 PopC: 3 HamD: 4
## 121 0000000001111001 PopC: 5 HamD: 4
## 91 0000000001011011 PopC: 5 HamD: 2
## 137 0000000010001001 PopC: 3 HamD: 4
## 103 0000000001100111 PopC: 5 HamD: 6
## 155 0000000010011011 PopC: 5 HamD: 6
## 233 0000000011101001 PopC: 5 HamD: 4
## 175 0000000010101111 PopC: 6 HamD: 3
## 263 0000000100000111 PopC: 4 HamD: 4
## 395 0000000110001011 PopC: 5 HamD: 3
## 593 0000001001010001 PopC: 4 HamD: 7
## 445 0000000110111101 PopC: 7 HamD: 7
## 167 0000000010100111 PopC: 5 HamD: 4
## 251 0000000011111011 PopC: 7 HamD: 4
## 377 0000000101111001 PopC: 6 HamD: 3
## 283 0000000100011011 PopC: 5 HamD: 3
## 425 0000000110101001 PopC: 5 HamD: 4
## 319 0000000100111111 PopC: 7 HamD: 4
## 479 0000000111011111 PopC: 8 HamD: 3
## 719 0000001011001111 PopC: 7 HamD: 3
##1079 0000010000110111 PopC: 6 HamD: 7
##1619 0000011001010011 PopC: 6 HamD: 4
##2429 0000100101111101 PopC: 8 HamD: 8
## 911 0000001110001111 PopC: 7 HamD: 7
##1367 0000010101010111 PopC: 7 HamD: 6
##2051 0000100000000011 PopC: 3 HamD: 6
##3077 0000110000000101 PopC: 4 HamD: 3
## 577 0000001001000001 PopC: 3 HamD: 5
## 433 0000000110110001 PopC: 5 HamD: 6
## 325 0000000101000101 PopC: 4 HamD: 5
## 61 0000000000111101 PopC: 5 HamD: 5
## 23 0000000000010111 PopC: 4 HamD: 3
## 35 0000000000100011 PopC: 3 HamD: 3
## 53 0000000000110101 PopC: 4 HamD: 3
## 5 0000000000000101 PopC: 2 HamD: 2
## 1 0000000000000001 PopC: 1 HamD: 1
##[1, 2, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 2,
## 1, 1, 1, 2, 3, 1, 1, 2, 1, 2, 1, 1, 1,
## 1, 1, 3, 1, 1, 1, 4, 2, 2, 4, 3, 1, 1,
## 5, 4]
 
G

George Sakkis

Thank you for the quick responses.
I did not know that about integer literals beginning with a '0', so
thank you for the explanation. I never really use PHP except for
handling basic forms and silly web stuff, this is why I picked up
Python because I want to teach myself a more powerful and broad
programming language.
With regard to why I asked: I wanted to learn about Binary math in
conjunction with Python, so I wrote a small function that would return
a base 10 number from a binary number. It is nice to know about the
int() function now.
Just for the sake of it, this was the function I came up with:
def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
res.reverse()
print sum(res)
Now that I look at it, I probably don't need that last reverse()
because addition is commutative...
def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)

Right idea: now to remove all those intermediate lists you construct.
1. reversed(val) creates an iterator that runs over the elements (here
of a string) in reverse order.
2. enumerate() is usually better than using an explicit list index.
3. You can use a generator in your sum to avoid constructing the final
list.

Applying these to your function, and noting that n << k is nicer than
n * 2 ** k, we get a one-liner:

def bin2dec(val):
return sum(int(i) << k for k, i in enumerate(reversed(val)))

Or a slightly nicer alternative is to filter the generator using 'if':

def bin2dec(val):
return sum(1 << k for k, i in enumerate(reversed(val)) if int(i))

Changing the condition to if i=='1' makes it than twice faster.
There's also a small improvement by looping forward rather than
reversed:

def bin2dec_2(val):
n = len(val)-1
return sum(1<<n-i for i,bit in enumerate(val) if bit=='1')

These one-liners are short and sweet and have decent performance for
most use cases. Still, for squeezing every last drop of performance
while staying in pure Python, use Psyco and a more verbose version.
The following is two orders of magnitude faster with Psyco enabled:

def bin2dec_3(val):
s = 0
p = 1 << len(val)
for bit in val:
p >>= 1
if bit == '1':
s += p
return s

And here's the benchmark:

if __name__ == '__main__':
# uncomment for Psyco
# import psyco; psyco.full()
import timeit
setup = 'import __main__; bin="101001010001001010"'
for func in bin2dec, bin2dec_2, bin2dec_3:
name = func.__name__
timer = timeit.Timer('__main__.%s(bin)' % name, setup)
print '%s: %s' % (name, timer.timeit())

### Without Psyco ####
bin2dec: 17.6126108206
bin2dec_2: 7.57195732977
bin2dec_3: 5.46163297291

### With Psyco ####
bin2dec: 17.6995679618
bin2dec_2: 8.60846224869
bin2dec_3: 0.16031255369

George
 
M

Michele Simionato

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)

It basically does the same thing int(string, 2) does.

Thank you for the responses!

BTW, here is the reverse function dec2bin, so that you can
bang your head on it for a while ;)

def baseN(number, N=2):
""" '1001'
"""
assert 2 <= N <= 10
assert isinstance(number, int) and number >= 0
b = []
while number:
b.append(str(number % N))
number /= N
return ''.join(reversed(b))

Michele Simionato
 
I

Ixiaus

Right idea: now to remove all those intermediate lists you construct.
1. reversed(val) creates an iterator that runs over the elements (here
of a string) in reverse order.
2. enumerate() is usually better than using an explicit list index.
3. You can use a generator in your sum to avoid constructing the final
list.

Applying these to your function, and noting that n << k is nicer than
n * 2 ** k, we get a one-liner:

def bin2dec(val):
return sum(int(i) << k for k, i in enumerate(reversed(val)))

Or a slightly nicer alternative is to filter the generator using 'if':

def bin2dec(val):
return sum(1 << k for k, i in enumerate(reversed(val)) if int(i))

Thank you for this reply, I only wish I could come up with functions
so elegantly refined!

I know '<<' is shifting x over by n bits; but could you point me to
some literature that would explain why it is the same as "x*2**n"?
(Googling only returns bit shift, but doesn't necessarily explain the
manner in which you are using it)

I will have to read up more on Generators, but maybe you can give me a
lowdown on why
sum([1 << k for k, i in enumerate(reversed(val)) if int(i)]) is less
efficient than using a Generator (is the generator a 'temporary'
list?)
sum(1 << k for k, i in enumerate(reversed(val)) if int(i))

-- Parnell Springmeyer
 
S

Stargaming

On Wed, 17 Oct 2007 22:05:36 +0200, Bruno Desthuilliers wrote:
[snip]
Note that there's also the reverse() function that returns a reverse
iterator over any sequence, so you could also do:

li = list('allo')
print ''.join(reverse(li))

Note this certainly should've been `reversed()`, with a trailing 'd'.
 
B

Bruno Desthuilliers

Stargaming a écrit :
On Wed, 17 Oct 2007 22:05:36 +0200, Bruno Desthuilliers wrote:
[snip]
Note that there's also the reverse() function that returns a reverse
iterator over any sequence, so you could also do:

li = list('allo')
print ''.join(reverse(li))

Note this certainly should've been `reversed()`, with a trailing 'd'.

2-0 for Stargaming. I'll have to either change glasses, buy a new
keyboard (this one is obviously broken), or learn to both use a keyboard
and re-read what I post.
 
A

Arnaud Delobelle

I know '<<' is shifting x over by n bits; but could you point me to
some literature that would explain why it is the same as "x*2**n"?

I haven't got literature but I've got a (hopefully straightforward)
explanation:

In binary 2 is 10. When you multiply by 10, you shift all your digits
left by 1 place.
When you multiply by 10**n (which is 1 followed by n zeroes), you
shift all your digits left by n places.

HTH
 
S

Steven D'Aprano

I know '<<' is shifting x over by n bits; but could you point me to some
literature that would explain why it is the same as "x*2**n"? (Googling
only returns bit shift, but doesn't necessarily explain the manner in
which you are using it)

In decimal, multiplying by ten shifts the digits to the left by one place:

31 * 10 => 310
310 * 10 => 3100
3100 * 10 => 31000

In binary, multiplying by two shifts the bits to the left by one place:

101 * 10 => 1010
1010 * 10 => 10100
10100 * 10 => 101000

Because the underlying numbers are the same regardless of what base we
use to write it in, bit-shifts to the left are equivalent to repeated
multiplication by two regardless of the display base. Hence:
56

or to do it another way:
56



Similarly, a bit-shift to the right is equivalent to dividing by two and
dropping any remainder.


I will have to read up more on Generators, but maybe you can give me a
lowdown on why
sum([1 << k for k, i in enumerate(reversed(val)) if int(i)]) is less
efficient than using a Generator (is the generator a 'temporary' list?)
sum(1 << k for k, i in enumerate(reversed(val)) if int(i))

The list comprehension:

[1 << k for k, i in enumerate(reversed(val)) if int(i)]

creates the entire list first. That potentially can require a lot of
memory, leading to all sorts of complicated memory shuffles as the list
is resized, paging, etc.

Using a generator expression means that you avoid creating the entire
list all in one go. That saves you memory, which may mean that you save
time.

However, for small sets of input, it is likely that the overhead of
creating the generator in the first place outweighs the saving of not
creating the whole list.

The definition of "small" depends on what you are trying to do. In my
tests, I have found that sum(list comprehension) is marginally faster
than sum(generator expression) even for 10,000 items.
 
M

MRAB

def bin2dec(val):
li = list(val)
li.reverse()
res = [int(li[x])*2**x for x in range(len(li))]
print sum(res)
It basically does the same thing int(string, 2) does.
Thank you for the responses!

BTW, here is the reverse function dec2bin, so that you can
bang your head on it for a while ;)
It returns '' when number == 0, so you need to test for that case:
def baseN(number, N=2):
"""
'1001'
"""
assert 2 <= N <= 10
assert isinstance(number, int) and number >= 0
if number == 0:
return "0"
b = []
while number:
b.append(str(number % N))
number /= N
return ''.join(reversed(b))

Michele Simionato
 
M

Michele Simionato

It returns '' when number == 0, so you need to test for that case:
def baseN(number, N=2):
"""
'1001'
"""
assert 2 <= N <= 10
assert isinstance(number, int) and number >= 0

if number == 0:
return "0"
b = []
while number:
b.append(str(number % N))
number /= N
return ''.join(reversed(b))

Right, or you can just change the last line:

return ''.join(reversed(b)) or '0'


Michele Simionato
 
H

Hendrik van Rooyen

Arnaud Delobelle said:
In binary 2 is 10. When you multiply by 10, you shift all your digits
left by 1 place.
When you multiply by 10**n (which is 1 followed by n zeroes), you
shift all your digits left by n places.

I read somewhere:

Only 1 person in 1000 understands binary.
The other 111 don't have a clue.

- Hendrik
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top