文章来自《Python cookbook》.

翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!

-- Zoom.Quiet [2004-08-11 00:50:03]

1. 用一个语句来赋值和测试

Assigning and Testing with One Statement Credit: Alex Martelli

1.1. 问题

1.10.1 Problem

You are transliterating C or Perl code to Python, and, to keep close to the original's structure, you need an expression's result to be both assigned and tested (as in if((x=foo( )) or while((x=foo( )) in such other languages).

你正在把c或者perl代码翻译到python, 并且,保持原始的结构。你需要一个即是赋值也是测试的表达式结果

(在那样的语言里,象这样if((x=foo( ))或者while((x=foo( ))) 

1.2. 解决

1.10.2 Solution

In Python, you can't code:

在python, 你不能写这样的代码:

   1 if x=foo( ):

Assignment is a statement, so it cannot fit into an expression, which is necessary for conditions of if and while statements. Normally this isn't a problem, as you can just structure your code around it. For example, this is quite Pythonic:

赋值是一个语句,所以它不适合一个表达式,这对if和while语句是一个必要条件。通常地,这不是一个问题,因为你能围绕它来构建你的代码。 例如,只是相当python化的

   1 while 1:
   2     line = file.readline(  )
   3     if not line: break
   4     process(line)

In modern Python, this is far better, but it's even farther from C-like idioms:

在现代python, 只是相当不错的。但是它更远离了类似c的惯用方法:

   1 for line in file.xreadlines(  ):
   2     process(line)

In Python 2.2, you can be even simpler and more elegant:

在python2.2,你甚至能更简单, 更优雅:

   1 for line in file:
   2     process(line)

But sometimes you're transliterating C, Perl, or some other language, and you'd like your transliteration to be structurally close to the original.

但是有时候,你正在翻译c, perl,或者其他一些语言。并且,你想让你的翻译的结构更接近于原始的。

One simple utility class makes this easy:

一个简单的可利用的类让它更容易:

   1 class DataHolder:
   2     def _ _init_ _(self, value=None):
   3         self.value = value
   4     def set(self, value):
   5         self.value = value
   6         return value
   7     def get(self):
   8         return self.value
   9 # optional and strongly discouraged, but handy at times:
  10 import _ _builtin_ _
  11 _ _builtin_ _.DataHolder = DataHolder
  12 _ _builtin_ _.data = DataHolder(  )

With the help of the DataHolder class and its data instance, you can keep your C-like code structure intact in transliteration:

DataHolder和它的数据实例的帮助下,你可以在你的翻译中,完整地保持类c的代码结构:

   1 while data.set(file.readline( )):
   2 process(data.get( ))

1.3. 讨论

1.10.3 Discussion

In Python, assignment is not an expression. Thus, you cannot assign the result that you are testing in, for example, an if, elif, or while statement. This is usually okay: you just structure your code to avoid the need to assign while testing (in fact, your code will often become clearer as a result). However, sometimes you may be writing Python code that is the transliteration of code originally written in C, Perl, or another language that supports assignment-as-expression. For example, such transliteration often occurs in the first Python version of an algorithm for which a reference implementation is supplied, an algorithm taken from a book, and so on. In such cases, having the structure of your initial transliteration be close to that of the code you're transcribing is often preferable. Fortunately, Python offers enough power to make it pretty trivial to satisfy this requirement.

在python中,赋值不是一个表达式。这样,你不能把赋值你正在测试的结果。例如,if, elif, 或者while语句都是这样的。这个是经常被认同的:当测试的时候,你仅仅构造你的代码去避免赋值的需要(实际上,这样的结果是你的代码将更清晰)。 然而,有时候,你需要把一些用c,perl或者其他语言写的原始代码翻译成python 代码,这些语言支持赋值作为表达式(assignment-as-expression)。 例如, 那样的翻译经常发生在第一个提供引用实现的算法的python版本,而这个算法是从一本书里摘抄的,等等情况。在那样情况下,你的最初翻译的数据结构接近于你正在翻译的代码是更可取的。 幸运的是,python提供足够的能力让它非常容易的去满足这个要求

We can't redefine assignment, but we can have a method (or function) that saves its argument somewhere and returns that argument so it can be tested. That "somewhere" is most naturally an attribute of an object, so a method is a more natural choice than a function. Of course, we could just retrieve the attribute directly (i.e., the get method is redundant), but it looks nicer to have symmetry between data.set and data.get.

我们不能重新定义赋值,但是我们有一个方法(或者函数)保存它的参数在一些地方,并且返回一个参数,所以它能被测试。“这样一些地方”最自然的是一个对象的属性。所以一个方法比一个函数是更自然的选择。当然,我们仅仅直接获得一个属性(get方法是多余的)。但是在data.set 和 data.get之间看上去有更好的对称性

Special-purpose solutions, such as the xreadlines method of file objects, the similar decorator function in the xreadlines module, and (not so special-purpose) Python 2.2 iterators, are obviously preferable for the purposes for which they've been designed. However, such constructs can imply even wider deviation from the structure of the algorithm being transliterated. Thus, while they're great in themselves, they don't really address the problem presented here.

特殊目的的解决方案, 诸如file对象的xreadlines方法,在xreadlines模块里有相似的decorator函数。并且(不是特殊目的)python2.2的iterators是一个很明显的是为了这个目的所设计的。然而,那样的构造甚至意味着更远离被翻译的算法的结构。因而,当他们是重中之重时,他们并不真正地处理在这儿表现出来的问题

data.set(whatever) can be seen as little more than syntactic sugar for data.value=whatever, with the added value of being acceptable as an expression. Therefore, it's the one obviously right way to satisfy the requirement for a reasonably faithful transliteration. The only difference is the syntactic sugar variation needed, and that's a minor issue.

data.set(whatever)被看做比data.value=whatever有稍多的语法修饰,增加的值被接受作为一个表达式。所以,对于忠实于翻译来说,它是很明显的正确的满足需求的方法。不同指出是语法修饰变化的需要,但是那是一个小问题

Importing _ _builtin_ _ and assigning to its attributes is a trick that basically defines a new built-in object at runtime. All other modules will automatically be able to access these new built-ins without having to do an import. It's not good practice, though, since readers of those modules should not need to know about the strange side effects of other modules in the application. Nevertheless, it's a trick worth knowing about in case you encounter it.

重要的_ _builtin_ _和赋值到它的属性是一个诀窍,它主要在运行时定义一个新的内建的对象.所有其他模块将自动能够防卫这些新的内建对象,而不要做一个import.它不是一个好的实践.尽管因为那些模块的读者不需要知道关于在一个应用中其他模块的副作用

Not recommended, in any case, is the following abuse of list format as comprehension syntax:

在任何情况下,都不推荐下面list格式作为包容语法的滥用

   1 while [line for line in (file.readline(),) if line]:
   2     process(line)

It works, but it is unreadable and error-prone.

这种方法行得通,但是它难以理解 并且容易出错.

1.4. 参考

See Also

The Tutorial section on classes; the documentation for the builtin module in the Library Reference.