当前位置: 澳门新濠3559 > 编程 > 正文

查看所有的关键字,代码中减少flag这类状态变量

时间:2019-11-09 19:38来源:编程
澳门新濠3559,翻看公司的代码文档,在代码风格文档中,写着:为了提高代码的可维护性,代码中减少flag这类状态变量的使用。这个问题,平时确实没有想过,面对这种需求时,第一

澳门新濠3559,翻看公司的代码文档,在代码风格文档中,写着:为了提高代码的可维护性,代码中减少flag这类状态变量的使用。这个问题,平时确实没有想过,面对这种需求时,第一反应就是使用flag标记状态。那么使用什么样的技巧或代码结构才能,才能代替状态变量的使用呢?我想到的方法是应用else。

Python 中的 else详解,pythonelse详解

我们都知道 Python 中else的基本用法是在条件控制语句中的 if...elif...else...,但是 else 还有两个其它的用途,一是用于循环的结尾,另一个是用在错误处理的 try 中。这原本是 Python 的标准语法,但由于和大部分其它编程语言的习惯不太一样,致使人们有意或无意地忽略了这些用法。另外,对于这些用法是否符合 0×00 The Zen of Python 的原则以及该不该广泛使用也存在很多争议。例如在我看到的两本书里(Effective Python VS Write Idiomatic Python),两位作者就分别对其持有截然不同的态度。

循环中的 else

跟在循环后面的 else 语句只有在当循环内没出现 break,也就是正常循环完成时才会执行。首先我们来看一个插入排序法的例子:

from random import randrange
def insertion_sort(seq):
  if len(seq) 1:
    return seq
  _sorted = seq[:1]
  for i in seq[1:]:
    inserted = False
    for j in range(len(_sorted)):
      if i _sorted[j]:
        _sorted = [*_sorted[:j], i, *_sorted[j:]]
        inserted = True
        break
    if not inserted:
      _sorted.append(i)
  return _sorted

print(insertion_sort([randrange(1, 100) for i in range(10)]))

[8, 12, 12, 34, 38, 68, 72, 78, 84, 90]

在这个例子中,对已排序的 _sorted 元素逐个与 i 进行比较,若 i 比已排序的所有元素都大,则只能排在已排序列表的最后。这时我们就需要一个额外的状态变量 inserted 来标记完成遍历循环还是中途被 break,在这种情况下,我们可以用 else 来取代这一状态变量:

def insertion_sort(seq):
  if len(seq) 1:
    return seq
  _sorted = seq[:1]
  for i in seq[1:]:
    for j in range(len(_sorted)):
      if i _sorted[j]:
        _sorted = [*_sorted[:j], i, *_sorted[j:]]
        break
    else:
      _sorted.append(i)
  return _sorted
print(insertion_sort([randrange(1, 100) for i in range(10)]))

[1, 10, 27, 32, 32, 43, 50, 55, 80, 94]

我认为这是一个非常酷的做法!不过要注意的是,除了 break 可以触发后面的 else 语句,没有循环的时候也会:

while False:
  print("Will never print!")
else:
  print("Loop failed!")


Loop failed!

错误捕捉中的 else

try...except...else...finally 流程控制语法用于捕捉可能出现的异常并进行相应的处理,其中 except 用于捕捉 try 语句中出现的错误;而 else 则用于处理没有出现错误的情况;finally 负责 try 语句的”善后工作“ ,无论如何都会执行。可以通过一个简单的例子来展示:

def divide(x, y):
  try:
    result = x / y
  except ZeroDivisionError:
    print("division by 0!")
  else:
    print("result = {}".format(result))
  finally:
    print("divide finished!")
divide(5,2)
print("*"*20)
divide(5,0)



result = 2.5
divide finished!
********************
division by 0!
divide finished!

当然,也可以用状态变量的做法来替代 else:

def divide(x, y):
  result = None
  try:
    result = x / y
  except ZeroDivisionError:
    print("division by 0!")
  if result is not None:
    print("result = {}".format(result))
  print("divide finished!")

divide(5,2)
print("*"*20)
divide(5,0)



result = 2.5
divide finished!
********************
division by 0!
divide finished!

总结

有人觉得 else 的这些用法违反直觉或者是 implicit 而非 explicit,不值得提倡。但我觉得这种”判决“需要依赖具体的应用场景以及我们对 Python 的理解,并非一定要对新人友好的语法才算是 explicit 的。当然也不推荐在所有地方都使用这个语法,for/while...else 最大的缺点在于 else 是需要与 for/file 对齐的,如果是多层嵌套或者循环体太长的情况,就非常不适合用 else(回忆一下游标卡尺的梗就知道了:P)。只有在一些简短的循环控制语句中,我们通过 else 摆脱一些累赘的状态变量,这才是最 Pythonic 的应用场景!

中的 else详解,pythonelse详解 我们都知道 Python 中else的基本用法是在条件控制语句中的 if...elif...else...,但是 else 还有两个其它的用途,...

查看关键字的方法

说到else,最常用的语法是if-elif-else,实际上else是可以与循环和try搭配使用的,下面我写了一个else替换状态变量的例子,使用插入法对数组排序。首先使用状态变量实现,代码如下:

查看所有的关键字:help("keywords")

def insertion_sort(input_list):
    if len(input_list) <= 1:
        return input_list
    _sorted = input_list[:1]

    for i in input_list[1:]:
        inserted = False
        for j in xrange(len(_sorted)):
            if i <= _sorted[j]:
                _sorted.insert(j, i)
                inserted = True
                break
        if not inserted:
            _sorted.append(i)
    return _sorted
>>> help("keywords")

Here is a list of the Python keywords.  Enter any keyword to get more help.

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not
class               from                or
continue            global              pass

使用inserted来标记新元素是否执行了插入操作,如果未执行的话,将元素插入到末位。

或者

下面给出使用else替换inserted的代码实现:

>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

 

关键字概述

关键字 含义
and 为逻辑关系用语,Python具有短路逻辑,不执行后面的语句,False and 返回 False
as 使用标准库中的模块,如import cPickle as p
assert 断言,声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。 可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。
break 提前跳出一个块
class 定义类
continue 回到一个块的开始处
def def关键字进行函数的定义,不用指定返回值的类型。
del 删除变量
elif 和if配合使用的,if语句中的一个分支用elif表示。
else 用在条件语句中,表明当条件不成立时的分支
except 和try一起使用,用来捕获异常。
False 数据类型bool(布尔)类型的实例对象(值),假
finally 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
for 一种循环结构的引导词
from 在python用import或者from...import来导入相应的模块
global golbal为全局变量,但当单个函数中出现同一变量名时,在单个函数中为局部变量
if 条件语句的引导词
import 导入相应的库
in 迭代器
is is 和 is not 是Python下判断同一性的关键字
lambda 即匿名函数,不用想给函数起什么名字。提升了代码的简洁程度。
None 空对象,空值是Python里一个特殊的值
nonlocal 用来在函数或其他作用域中使用外层(非全局)变量
not 布尔的非
pass 空语句 do nothing,保证格式完整,保证语义完整
raise 触发异常后,后面的代码就不会再执行
return 从成员方法中返回数据
True 数据类型bool(布尔)类型的实例对象(值),真
or 为逻辑关系用语,Python具有短路逻辑,不执行后面的语句, True or 直接返回True,
try 尝试一个可能抛出异常的程序块
while 用在循环结构中
with with被用来处理异常
yield 意思是生产,返回了一个生成器对象,每个生成器只能使用一次
def insertion_sort1(input_list):
    if len(input_list) <= 1:
        return input_list
    _sorted = input_list[:1]

    for i in input_list[1:]:
        for j in xrange(len(_sorted)):
            if i <= _sorted[j]:
                _sorted.insert(j, i)
                break
        else:
            _sorted.append(i)
    return _sorted

判断、循环

Python的循环及判断主要包括这些关键字:
if  elif  else  for  while  break  continue  and   or  is  not  in

if 语法、while语法、for语法以及and...or语法

 

if 语法

  • if-else的使用格式
if 条件:
    满足条件时要做的事情1
    ...(省略)...
else:
    不满足条件时要做的事情1
    ...(省略)...
  • elif的功能
if 条件1:
    满足条件1时要做的事情
    ...
elif 条件2:
    不满足条件1,满足条件2时要做的事情
    ...
else:
    不满足条件1,2时要做的事情
    ...

当for循环执行完整,中途没有break的话,就会执行else操作,也就起到了和inserted相同的作用。

while语法

while 条件:
    条件满⾜时,做的事情1
    条件满⾜时,做的事情2
    条件满⾜时,做的事情3
    ...(省略)...

else在try...except...else...finally的流程控制语法中同样适用,当程序没走进except时,就会使用else 中的代码,我们就不需要定义状态变量来记录程序是否走入except,代码如下:

for语法

for 临时变量 in 列表或者字符串等:
    循环满⾜条件时执⾏的代码
else:
    循环不满⾜条件时执⾏的代码

break的作⽤:⽤来结束整个循环
continue的作⽤:⽤来结束本次循环,紧接着执⾏下⼀次的循环

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("error: division by 0!")
    else:
        print "result = %s" % result
    finally:
        print("divide finished!")

and...or语法

   Python的and/or操作与其他语言不同的是它的返回值是参与判断的两个值之一,所以我们可以通过这个特性来实现Python下的 a ? b : c !

  Python下我们可以这么用:“ a and b or c ”(此方法中必须保证b必须是True值),python自左向右执行此句,先判断a and b :如果a是True值,a and b语句仍需要执行b,而此时b是True值!所以a and b的值是b,而此时a and b or c就变成了b or c,因b是True值,所以b or c的结果也是b;如果a是False值,a and b语句的结果就是a,此时 a and b or c就转化为a or c,因为此时a是 False值,所以不管c是True 还是Flase,a or c的结果就是c

当bool and a or b相当于bool? a: b
注:但是特殊情况是,若a为假,则不会相当于,而是返回b
解决办法:用 (bool and [a] or [b])[0]代替

因为每个人的变成习惯不同,这种python独有的语法并不被提倡,而且如果代码嵌套了多层循环,那么使用else代替状态变量也降低了代码的可读性。但是如果python使用足够熟练,在一些简短的循环控制语句中,我们通过else摆脱一些累赘的状态变量,还是可取的。

函数、模块、类

对于Python的函数及模块主要包括这些关键字:
from  import  as  def  pass  lambda  return  class

 

模块

  Python的编程通常大量使用标准库中的模块,使用方法就是使用import 、from以及as 关键字。

比如:

import sys  # 导入sys模块
from sys import argv   # 从sys模块中导入argv ,这个在前面介绍脚本传参数时使用到
import cPickle as p     # 将cPickle模块导入并在此将它简单命名为p,此后直接可以使用p替代cPickle模块原名,这个在介绍文件输入输出时的存储器中使用到
  • from...import

如 from A import b,相当于
  import A
  b=A.b

再如:
"from t2 import var1" 相当于:
  import t2
  var1= t2.var1
在此过程中有一个隐含的赋值的过程

  • import......as

  import A as B,给予A库一个B的别称,帮助记忆

函数

Python中定义函数时使用到def关键字,如果你当前不想写入真实的函数操作,可以使用pass关键字指代不做任何操作

def

def function(params):
    block
    return expression/value
  • 在Python中采用def关键字进行函数的定义,不用指定返回值的类型。

  • 函数参数params可以是零个、一个或者多个,同样的,函数参数也不用指定参数类型,因为在Python中变量都是弱类型的,Python会自动根据值来维护其类型。

  • return语句是可选的,它可以在函数体内任何地方出现,表示函数调用执行到此结束;如果没有return语句,会自动返回NONE,如果有return语句,但是return后面没有接表达式或者值的话也是返回NONE。

pass

  • 空语句 do nothing
  • 保证格式完整
  • 保证语义完整

pass语句在函数中的作用

  当你在编写一个程序时,执行语句部分思路还没有完成,这时你可以用pass语句来占位,也可以当做是一个标记,是要过后来完成的代码。比如下面这样:

def iplaypython():
    pass

  定义一个函数iplaypython,但函数体部分暂时还没有完成,又不能空着不写内容,因此可以用pass来替代占个位置。

pass语句在循环中的作用

  pass也常用于为复合语句编写一个空的主体,比如说你想一个while语句的无限循环,每次迭代时不需要任何操作,你可以这样写:

while True:
    pass

  以上只是举个例子,现实中最好不要写这样的代码,因为执行代码块为pass也就是空什么也不做,这时python会进入死循环。

Lambda

  lambda的一般形式是关键字lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式。lambda是一个表达式而不是一个语句。它能够出现在Python语法不允许def出现的地方。作为表达式,lambda返回一个值(即一个新的函数)。lambda用来编写简单的函数,而def用来处理更强大的任务。

f = lambda x,y,z : x+y+z  
print f(1,2,3)  #6

g = lambda x,y=2,z=3 : x+y+z  
print g(1,z=4,y=5)  #10

异常

对于Python的异常主要包括这些关键字:
try  except  finally  raise
捕捉异常可以使用try/except语句。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。

try:
<语句>        #运行别的代码
except <名字>:
<语句>        #如果在try部份引发了'name'异常
except <名字>,<数据>:
<语句>        #如果引发了'name'异常,获得附加的数据
else:
<语句>        #如果没有异常发生

  try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

  • 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。

  • 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。

  • 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。

raise关键字是在一定的情况下引发异常,通常结合自定义的异常类型使用。

其他

print  del  global  with  assert  yield  exec

del

  由于python都是引用,而python有GC机制,所以,del语句作用在变量上,而不是数据对象上。del删除的是变量,而不是数据。

global

  当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。

# Filename: func_local.py  

def func(x):  
    print 'x is', x  
    x = 2  
    print 'Changed local x to', x  

x = 50  
func(x)  
print 'x is still', x  

运行的结果是这样的:

[python] view plain copy
$ python func_local.py  
x is 50                     # 运行func函数时,先打印x的值,此时带的值是作为参数带入的外部定义的50,所以能正常打印 x=50   
Changed local x to 2        # 在func函数中将x赋2,并打印  
x is still 50               # 运行完func函数,打印x的值,此时x的值仍然是之前赋给的50,而不是func函数中修改过的2,因为在函数中修改的只是函数内的局部变量  

  那么为什么我们要在这提到局部变量呢?bingo,聪明的你一下就猜到这个global就是用来定义全局变量的。也就是说如果你想要为一个在函数外定义的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是 全局 的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。
  只是函数内的局部变量

# Filename: func_global.py  

def func():  
    global x  
    print 'x is', x  
    x = 2  
    print 'Changed local x to', x  

x = 50  
func()  
print 'Value of x is', x  
只是函数内的局部变量  

 运行的结果是这样的:
 只是函数内的局部变量

$ python func_global.py  
x is 50  
Changed global x to 2  
Value of x is 2             # global语句被用来声明x是全局的——因此,当我们在函数内把值赋给x的时候,这个变化也反映在我们在主块中使用x的值的时候。  
只是函数内的局部变量  

with 关键字

  有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。

  如果不用with语句,打开一个文件并读文件的代码如下:

file = open("/tmp/foo.txt")  
data = file.read()  
file.close()  

  当然这样直接打开有两个问题:一是可能忘记关闭文件句柄;二是文件读取数据发生异常,没有进行任何处理。下面是添加上异常处理的版本:

file = open("/tmp/foo.txt")  
try:  
    data = file.read()  
finally:  
    file.close()  

  虽然这段代码运行良好,但是太冗余了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常。下面是with版本的代码:

with open("/tmp/foo.txt") as file:  
    data = file.read()  

  这看起来充满魔法,但不仅仅是魔法,Python对with的处理还很聪明。基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。
  with语句的执行逻辑如下:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。

下面例子可以具体说明with如何工作:

# with_example01.py  

class Sample:  
    def __enter__(self):  
        print "In __enter__()"  
        return "Foo"  

    def __exit__(self, type, value, trace):  
        print "In __exit__()"  

def get_sample():  
    return Sample()  

with get_sample() as sample:  
    print "sample:", sample  

运行代码,输出如下

$python with_example01.py     
In __enter__()           # __enter__()方法被执行  
sample: Foo              # __enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample',执行代码块,打印变量"sample"的值为 "Foo"  
In __exit__()            # __exit__()方法被调用  

assert 关键字

  assert语句是一种插入调试断点到程序的一种便捷的方式。assert语句用来声明某个条件是真的,当assert语句失败的时候,会引发一AssertionError,所以结合try...except我们就可以处理这样的异常。

>>> mylist         # 此时mylist是有三个元素的列表
['a', 'b', 'c']
>>> assert len(mylist) is not None  # 用assert判断列表不为空,正确无返回
>>> assert len(mylist) is  None     # 用assert判断列表为空
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module>
AssertionError                      # 引发AssertionError异常

yield 关键字

我们先看一个示例:

def fab(max):   
   n, a, b = 0, 0, 1   
   while n < max:   
       yield b   
       # print b   
       a, b = b, a + b   
       n = n + 1   

使用这个函数:

>>> for n in fab(5):   
...     print n   
...   
1   
1   
2   
3   
5  

  简单地讲,yield 的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable(可迭代的)对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
  也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

>>> f = fab(5)   
 >>> f.next()   
 1   
 >>> f.next()   
 1   
 >>> f.next()   
 2   
 >>> f.next()   
 3   
 >>> f.next()   
 5   
 >>> f.next()   
 Traceback (most recent call last):   
  File "<stdin>", line 1, in <module>   
 StopIteration  

  当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。

  我们可以得出以下结论:  

  一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

  注:如果看完此段你还未明白yield,没问题,因为yield是初学者的一个难点,那么你下一步需要做的就是……看一看下面参考资料中给的关于yield的博文!

exec 关键字

  官方文档对于exec的解释: "This statement supports dynamic execution of Python code."也就是说使用exec可以动态执行Python代码(也可以是文件)

>>> longer = "print "Hello World ,my name is longer""     # 比如说我们定义了一个字符串  
>>> longer  
'print "Hello World ,my name is longer"'  
>>> exec(longer)                                            # 使用exec 动态执行字符串中的代码  
Hello World ,my name is longer  
>>> exec(sayhi)                                             # 使用exec直接打开文件名(指定sayhi,sayhi.py以及"sayhi.py"都会报一定的错,但是我觉得直接带sayhi报错非常典型)  
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
TypeError: exec: arg 1 must be a string, file, or code object   # python IDE报错,提示exec的第一个参  
数必须是一个字符串、文件或者一个代码对象  
>>> f = file("sayhi.py")                                    # 使用file打开sayhi.py并创建f实例  
>>> exec(f)                     # 使用exec直接运行文件描述符f,运行正常!!  
Hi, this is [''] script  

  上述给的例子比较简单,注意例子中exec语句的用法和eval_r(), execfile()是不一样的. exec是一个关键字而eval_r()和execfile()则是内建函数。

编辑:编程 本文来源:查看所有的关键字,代码中减少flag这类状态变量

关键词: