这是我的
@pythonetc feed中的第二组Python技巧和编程。 先前的选择:
常规语言
常规语言是一种形式语言,可以表示为
有限状态机 。 换句话说,对于逐个字符的文本处理,您只需要记住当前状态,此类状态的数量是有限的。
一个完美的例子:一台检查输入是否为–3、2.2或001之类的素数的机器。在本文的开头,显示了一个有限状态机。 双圆圈表示机器可以停止的最终状态。
机器从位置开始。 也许它找到一个负号,然后一个数字,然后在位置③处理所需的数字位数。 之后,可以检查小数点分隔符(③→④),然后检查一位数字(④→⑤)或更大一位(⑤→⑤)。
不规则语言的一个典型示例是以下形式的字符串表达式族:
ab
aaa-bbb
aaaaa-bbbbb
形式上,我们需要一个包含N个b实例的字符串,然后
–
,然后-N个
b
实例,其中N是大于0的整数。您不能使用状态机来实现它,因为您将不得不记住您认为可以的字符数仅使用无限多个状态即可完成。
正则表达式只能指定正则语言。 在使用它们之前,请确保甚至可以使用状态机来处理您的字符串。 例如,它们不适用于处理JSON,XML甚至带括号的算术表达式。
有趣的是,许多现代正则表达式引擎不是常规的。 例如,Python的regex模块支持递归(这
将有助于解决
aaa-bbb
的问题)。
动态排程
当Python调用方法
af(b, c, d)
,它必须首先选择正确的函数
f
。 凭借多态性,
a
决定了最终选择的内容。 选择方法的过程通常称为动态调度。
Python仅支持单调度多态性。 这意味着只有对象本身会影响对象的选择(在我们的示例中为
a
)。 在其他语言中,可以考虑类型
b
,
c
和
d
这种机制称为多重调度。 一个明显的例子是C#语言。
但是,可以使用一个调度表模拟多个调度表。 这就是创建访问者设计模板的原因:它使用一次分配两次来模拟两次。
请记住,重载方法(如Java和C ++)不是多重调度的类推。 动态调度在运行时起作用,并且重载仅在编译期间执行。
这些示例将帮助您更好地理解以下主题:
内联名称
在Python中,您可以轻松修改全局范围内可用的所有标准变量:
>>> print = 42 >>> print(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not callable
如果您的模块定义名称与内置函数名称匹配的函数,这将很有用。 在您练习元编程并采用任意字符串值作为标识符的情况下,也会发生这种情况。
但是,即使重复某些内置函数的名称,也可能需要访问它们最初引用的名称。 这就是内置模块存在的原因:
>>> import builtins >>> print = 42 >>> builtins.print(1) 1
在大多数模块中,还可以使用
__builtins__
变量。 但是有一个窍门。 首先,这是cpython实现的功能,通常完全不应该使用。 其次,
__builtins__
可以同时引用
builtins
和
builtins.__dict__
,这取决于当前模块的加载方式。
痕迹
有时,应用程序在战斗中表现出异常。 除了可能重新启动之外,您可能希望了解问题的原因。
显而易见的解决方案是分析程序的动作并尝试了解正在执行代码的哪一部分。 正确的日志记录使此任务更加容易,但是由于架构或设置中选择的日志记录级别,您的日志可能不够详细。
在这种情况下,strace可能有用。 这是一个Unix实用程序,用于跟踪系统调用。 您可以以前运行它
strace python script.py
但通常连接到已经运行的应用程序更方便:
strace -p PID
。
$ cat test.py with open('/tmp/test', 'w') as f: f.write('test') $ strace python test.py 2>&1 | grep open | tail -n 1 open("/tmp/test", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
跟踪的每一行都包含系统调用的名称,方括号中的参数以及返回值。 由于一些参数用于返回系统调用的结果,而不是将数据传递给该参数,因此可以暂停行输出,直到系统调用结束。
在此示例中,输出停止,直到完成对STDIN的写入:
$ strace python -c 'input()' read(0,
元组文字
Python语法中最不一致的部分之一是元组文字。
要创建一个元组,列出用逗号分隔的值就足够了:
1, 2, 3
。 单元素元组呢? 只需添加一个逗号即可:
1,
,.。 它看起来很丑陋,并经常导致错误,但这是合乎逻辑的。
空的元组怎么样? 这是一个逗号-,? 不,这是
()
。 而且,方括号会创建一个元组,例如逗号? 不,
(4)
不是元组,而只是
4
。
In : a = [ ...: (1, 2, 3), ...: (1, 2), ...: (1), ...: (), ...: ] In : [type(x) for x in a] Out: [tuple, tuple, int, tuple]
为了使事情更加混乱,元组文字通常需要额外的括号。 如果您需要元组作为函数的唯一参数,那么显然
f(1, 2, 3)
不起作用-您将不得不编写
f((1, 2, 3))
。