适合Java开发者学习的Python入门教程
作者:网络转载 发布时间:[ 2017/7/4 10:28:57 ] 推荐标签:测试开发技术 Java Python
def log_calls(old_function):
def new_function(*args, **kwargs):
print("i'm being called!", args, kwargs)
return old_function(*args, **kwargs)
return new_function
@log_calls
def foo(a, b, c=3):
print(f"a = {a}, b = {b}, c = {c}")
foo(1, b=2)
# i'm being called! (1,) {'b': 2}
# a = 1, b = 2, c = 3
这有点难以理解,对不起。 不用担心它究竟是如何工作的; 要点是,foo被一个new_function替换,它将所有的参数转发到foo。 foo和调用者都不需要知道哪些事情有哪些不同。
我不能低估它有多么强大。 它可用于记录,调试,管理资源,缓存,访问控制,验证等。 它与其他元编程功能工作良好,同样地,它可以让您分解结构,而不仅仅是代码。
对象和动态运行时
动态运行时是一种在背后驱动语言核心部分的东西 — 它可以在运行时被执行。像 C 或者 C++ 这类的语言绝不会具有动态运行时;它们源码的结构被“烘焙”成编译输出,并且后续没有明显的方法来改变它的行为。从另一方面,Java的确具有动态运行时!它甚至带有一整个专门用于反射的包。
Python 当然也有反射。很多简单的函数通过反射被构建,用来联机检查或修改对象的属性,这对调试以及偶尔的欺骗非常有用。
但 Python 对此更深入一点。 因为一切都在运行时完成,Python 暴露了很多扩展点来自定义它的语义。 你不能改变语法,代码依然看起来像 Python,但你可以分解结构 — 这在一种更死板的语言是非常难以做到的。
举个极端的例子,看下 pytest, 它聪明的处理了 Python 的 assert 声明。 通常, assert x == 1 为 false 时只会简单的抛出一个 AssertionError 异常,导致你不知道错误是什么或者哪里出错了。这是为什么 Python 内置的单元测试模块 — 像 JUnit 和很多其他的测试工具 — 提供很多专门的工具函数,比如 assertEquals。不幸的是,这些工具函数使得测试初看到时,更加复杂更难以读懂。但在 pytest 中, assert x == 1 是好用的。如果失败,pytest 将告诉你 x 是… 或者两个列表哪里有分歧,或者两个集合哪些元素不同, 或者其他的。所有的这些都是基于比较完成和运算对象的类型自动发生的。
pytest是如何工作的呢? 你确实不想知道。你不需要知道如何用pytest写测试 — 这使人很开心。
这是动态运行时的真正优势所在。 自己而言,可能没有使用这些功能。但是你能从这些库中收获巨大的好处,你使用这些库并不需要关心它们如何运行。 甚至 Python 本身依靠使用自己的扩展点实现了很多额外的特性 — 这些不需要改变语法或者解释器。
对象
属性(在C#中一般翻译成特性)存取是我喜欢举的一个简单例子。在 Java 中,一个 Point 类可能选择用 getX() 和 setX() 方法而不是一个简单直白的 x 属性。原因是如果你需要改变 x 的读或写,你不需要破坏接口。在 Python 中,你不需要担心前面的这些, 因为必要时你能解释属性存取。
class Point:
def __init__(self, x, y):
self._x = x
self._y = y
@property
def x(self):
return self._x
# ... same for y ...
point = Point(3, 4)
print(point.x) # 3
有趣的 @property 语法是一种修饰,看来像 Java 的注解,但它可以更直接地修改函数或类。这是完全透明的调用代码——和读其它属性没什么区别——但是对象可以根据自己的需要干预或处理它。与 Java 不同,属性访问是类 API 的一部分,可以自由的定义。(注意这个示例让 x 成为只读的,因为我没有指定写方法!可写属性的语法有点滑稽,这里暂时不管它如何工作。但你可以具体规定只有奇数可以赋值给 point.x。)
这个特性也存在于其它静态语言中,比如 C#,所以这并不是什么大不了的东西。关于 Python 真正有趣的部分是,属性并没什么特别。它是一个正常的内建类型,一段纯粹而且不满一屏的 Python 程序。它的工作原理是 Python 类可以自定义其属性访问,包括一般的和按属性的。包装、代理和组合很容易实现:你可以将所有访问调用转发到底层对象,而不必知道它有什么方法。
相同的钩子属性可用于懒加载属性或者自动持有弱引用的属性——这对调用代码完全透明,通过纯 Python 能实现。
你可能已经注意到我的代码没有使用 public 或 private 修饰符。事实上,Python 中不存在这个概念。按照惯例,用一个下划线开头表示“私有”——或者更准备地说,“不打算成为稳定公开 API 的一部分”。但这并没有语言上的意义,Phthon 本身不会阻止侦查或修改这样的属性(如果是方法的话,则是调用)。同样,也没有final 、static或const。
这是同样的工作原理:核心 Python 通常不会妨碍你做任何事情。如果你需要,它会非常有用。我已经通过启动时调用或重写,甚至重新定义私有方法来修补第三方库的 BUG。这样我不需要重新做一个项目的本地分支,可以省不少事。而且一量官方修复了 BUG,可很容易能删掉自己的补丁代码。
同样,你可以轻松地编写依赖外部状态的测试代码——比如当前时间。如果重构不可行,你可以在测试时把 time.time() 替换为模拟函数。库函数只是模块模块的属性(像 Java 的包),而且 Python 模块是种对象,和其它对象一样,所以它们可以以同样的方式侦查到或被修改。
类
Java 的类由 Class 对象支持,但二者并不有完全互换。比如 Foo 类的 Class 对象是 Foo.class。我不认为 Foo 可以被它自己使用,因为它命名了一个类型,Java 在处理类型和值的时候会有一些微妙的区别。
Python 中,类是对象,是类型的实例(它本身是对象,是它自己的实例,想起来很有趣。)类可以当作其它值一样使用:作为参数、保存一某个更大的数据结构中、检查、操作。有时候把类作为字典的键特别有用。而且因为类是实例化的,可以简单地调用它们——Python 没有 new 关键字 —— 很多情况下 new 和简单的函数调用可以互换。这样一来,一些常见的模式,比如工厂模式,太简单了,几乎不需要。
# 等等,Vehicle 是一个类还是一个工厂函数?谁管呢!
# 算它在类或工厂函数之间互换,也不会破坏这段代码。
car = Vehicle(wheels=4, doors=4)
近我好几次把函数甚至常规代码放在顶层,不在任何类里面。这样做不会有问题,但是其含义有点微妙。Python 中甚至 class 和 def 都是常规代码,在运行的时候执行。Python 文件从上往下执行,class 和 def 并不特别。它们只是有特殊的语法,用来创建特定类型的对象:类和函数。
以下是真正酷的部分。类是对象,它们的类型是type,因此你可以子类化并改变它的运行。然后,你可以生成你的子类的实例。
第一次接触仔细想想会感觉有点奇怪。但是再想下,你获益于不需要知道它是如何运行的。比如,Python没有enum块,但它确实有 enum module:
class Animal(Enum):
cat = 0
dog = 1
mouse = 2
snake = 3
print(Animal.cat) # <Animal.cat: 0>
print(Animal.cat.value) # 0
print(Animal(2)) # <Animal.mouse: 2>
print(Animal['dog']) # <Animal.dog: 1>
class 语句创建了一个对象,这意味着它在某处调用了构造函数,而且可以重写该构造函数来改变类的构造方式。这里Enum创建了一个固定的实例集,而不是类属性。所有这些都是用普通的 Python 代码的常规的 Python 语法实现的。
实体库也是基于这个思路来构建的。你讨厌在构造函数中单调的干 self.foo = foo 这种事情吗?然后纯手工定义相等性比较、哈希和克隆和开发可读的列表?Java 需要编译器支持,这个支持可能来自 Amber 项目。Python 非常灵活,所以社区中有 attrs 库解决了这个问题。
import attr
@attr.s
class Point:
x = attr.ib()
y = attr.ib()
p = Point(3, 4)
q = Point(x=3, y=4)
p == q # True, which it wouldn't have been before!
print(p) # Point(x=3, y=4)
或者采用 SQLAlchemy 这个功能强大的 Python 数据库封装库。它包含一个灵感来自 Hibernate 的 ORM,但不需要在配置文件里定义表结构或者通过其他冗长的注解,你可直接在类里编写数据库映射代码:
class Order(Table):
id = Column(Integer, primary_key=True)
order_number = Column(Integer, index=True)
status = Column(Enum('pending', 'complete'), default='pending')
...
它的基本思想类同Enum,但SQLAlchemy也使用和property同样的钩子,自然而然地,你可以修改栏位值。
order.order_number = 5
session.commit()
后,类本身可以在运行中被创建。 这有点好处,可是 thriftpy 创建的整个 module, 里面全是基于 Thrift 定义文件的类。 在Java中,你得需要代码生成,这增加了全新的编译步骤,从而导致不同步。
所有这些示例依赖于Python现存的语法,但也在里面吸收了新的含义。它能做的,没有你在Java或者其他语言中不能做的。但它删减了结构性的重复 — 正是这些使得编码更易写、易读,以及产生更少的bug。
结语
Python 有很多和Java相同的基本概念,但处理方向非常不同,它加入了一些全新的思想。Java关注于稳定性和可靠性,而Python关注于可表达性和灵活性。它是一种完全不同的思想方式来思考命令式编程。
我有理由相信Python将让你在Java所擅长的领域替换Java。Python可能不会赢在速度竞赛上,比如(Pypy,一种即时编译的Python)。Java拥有对线程的原生支持,而Python界大都回避了这些问题。规模庞大复杂又有很多死角的软件更喜欢静态类型提供的合理性检查(比如mypy,一种Python静态类型检查器)。
也许某些 Java 并不擅长的领域刚好是Python的强项。例如大量的软件对性能的要求并不高,也不需要并行执行,我们只需要考虑具体实现业务的问题。我发现使用 Python 开始一个项目非常简单而且快速。没有单独的编译过程,编码然后运行的速度非常快。代码很简短,这也意味着代码更容易被理解。尝试不同的架构方法看起来更加容易。而且有时候尝试一些愚蠢的想法是如此的有趣,好像 使用库实现 goto 功能。
我希望你能试试 Python ,自从我用了它之后,已经从中获得很多乐趣,我相信你也一样。至少不用刻意的去拒绝它。
坏的情况,还有个 Pyjnius 可以让你这样做:
from jnius import autoclass
System = autoclass('java.lang.System')
System.out.println('Hello, world!')
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南