引发自:Py的几处不爽

讨论习惯势力在Py 中的思路

::-- ZoomQuiet [2005-06-10 01:34:11]

发件人: flyaflyaa <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-8 下午2:29
主题: [python-chinese] py的几处不爽

1. 揭题

一、 
 >>> a = range(10)
 >>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 >>> for i in a:
            i += 1
 >>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
如果a中元素是数,用for就不能改变a中元素,只能用filter,map,reduce作复杂
处理了,如果a中元素是list,就可以。不统一。

二、
a = "abccdd"
a[0] = 'c"
出错,想改a中的一个元素就要再生成一个新的string,这多慢

三、
函数默认参数是list的话,每次调用都用同一个list,会发生错误。
 >>> def  foo( a = []):
    a.append(10)
    print a
 >>> foo()
[10]
 >>> foo()
[10, 10]
 >>>
这个问题影响不大,可为什么不改掉,很容易产生错误

2. 讨论

== i++ =

要是有 i++ 就好了..;) 

>>> i=1
>>> i+=2
>>> i
3
>>> i++
SyntaxError: invalid syntax
>>> ++i
3
>>> ++i
3
>>> i+
SyntaxError: invalid syntax
>>> +i
3
>>> i++i
6
>>> i++++i
6
>>>

2.1. 回答

发件人: Qiangning Hong <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-9 下午3:37
主题: Re: [python-chinese] py的几处不爽

lyaflyaa wrote:
> 一、
>>>> a = range(10)
>>>> a
> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>> for i in a:
>            i += 1
>>>> a
> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> 如果a中元素是数,用for就不能改变a中元素,只能用filter,map,reduce作复杂
> 处理了,如果a中元素是list,就可以。不统一。

这是因为你没有理解+=操作符和名字绑定的概念。
当i是数字时:i += 1 --> i = i + 1, 这是对"i"这个名字进行重新绑定。
当i是list时:i += [1] --> i.extend([1]),调用的是i的方法,可以改变i的值。
你如果用i = i + [1]就都不会修改a的值了。


> 二、
> a = "abccdd"
> a[0] = 'c"
> 出错,想改a中的一个元素就要再生成一个新的string,这多慢

string都是immutable的,不然不能作为dict的key。
如果你不能一次创建string,请先使用list,把要改的东西都改好了,再使用join
生成string。
如果你一定想要mutable string,请仔细阅读:help(UserString.MutableString)

google python mutable string,你会得到更多资料。

> 三、
> 函数默认参数是list的话,每次调用都用同一个list,会发生错误。
>>>> def  foo( a = []):
>    a.append(10)
>    print a
>>>> foo()
> [10]
>>>> foo()
> [10, 10]
>>>>
> 这个问题影响不大,可为什么不改掉,很容易产生错误

默认参数是在定义函数时而不是在调用时创建的。这是feature不是bug。

2.1.1. const行为

发件人: cpunion <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-9 下午4:12
主题: Re: [python-chinese] py的几处不爽

python把一些基本类型(包括string)都实现为const,模拟出const行为,即自身值
不变,每个操作都会生成一个新对象。

它并没有什么不统一,恰恰相反,它非常统一,遍历时确实返回了元素的引用,整
数值没有改变的原因是,整数的add操作没有改变它自身,而是返回另一个对象,
可以写一个非常简单的模拟:

>>> class Integer:
   def __init__ (self, value):
       self.value = value
   def __add__ (self, value):
       self.value += value
       return self
   def getValue (self):
       return self.value

>>> class ConstInteger:
   def __init__ (self, value):
       self.value = value
   def __add__ (self, value):
       return ConstInteger (self.value + value)
   def getValue (self):
       return self.value

>>> a = [Integer(i) for i in range (10)]
>>> for i in a:
   i += 1

>>> for i in a:
   print i.getValue ()

1
2
3
4
5
6
7
8
9
10
>>> b = [ConstInteger(i) for i in range (10)]
>>> for i in b:
   i += 1

>>> for i in b:
   print i.getValue ()

0
1
2
3
4
5
6
7
8
9

可见并没有不统一的地方,它也能根据需求做出不同的实现,恰好证明这方面是很
好用的。

2.1.2. add行为

发件人: flyaflyaa <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-8 下午6:07
主题: Re: [python-chinese] py的几处不爽

有些明白了,所有整数运算都产生新值,包括付值运算:
>>> a = 10
>>> b = a
>>> id(a), id(b)
(8017228, 8017228)
>>> a = 20
>>> id(a), id(b)
(8017108, 8017228)
>>>

所以用for虽然改变值了,但值在新的位置,list本身并不知道这个位置。
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in range(10):
   print id(a[i])

8017348
8017336
8017324
8017312
8017300
8017288
8017276
8017264
8017252
8017240
>>> a[0] = 100
>>> for i in range(10):
   print id(a[i])

10214556
8017336
8017324
8017312
8017300
8017288
8017276
8017264
8017252
8017240
>>>
所以只有用a[i],才能使list知道新值的位置。

2.1.2.1. cpunion

发件人: cpunion <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-9 下午6:22

i只是个引用名字,当调用i=...时,它会指向不同的对象,所以i+=1时,它所引用
的对象其实是变化了的。但i是list时,为何处理方式不一样呢(我已经推翻前面
的看法,和帖主看法一样了)?

我查看了一点python源码,+=运算符在内部其实是做了不同处理,对于数字类型
(或其它普遍类型),大概会转换成i = i + ..;对于序列类型,它会并不转换成
i = i + ..而是调用concat直接处理操作符左侧的对象。

+=操作符好像没办法重写?

2.1.2.2. limodou

发件人: limodou <[email protected]> 
回复: limodou <[email protected]>, [email protected]
收件人: [email protected]
日期: 2005-6-9 下午7:55

+= 相当于执行了 __iadd__( self, other) 
看 Python Reference Manual 中的3.3.7 Emulating numeric types

2.1.2.3. Qiangning Hong

发件人: Qiangning Hong <[email protected]> 
回复: [email protected]
收件人: [email protected]
日期: 2005-6-9 下午8:05

x+=y 会被解析成 x.__iadd__(y),如果x没有__iadd__方法则解析成x = x.__add__(y) 

list有__iadd__方法,int没有,这就是差别。

重载+=操作符可以通过重载__iadd__方法实现。详细请看Python Reference
Manual中3.3.7节:Emulating numeric types。