面向 JavaScript 开发人员的 Python
面向 JavaScript 开发人员的 Python
JavaScript 是我的面包和黄油,但我一点也不讨厌其他编程语言。我一直很喜欢 Python,多年来我一直使用它,从脚本到 Flask 和 Django 的 API 应用程序。
在这篇文章中,我将引导您对这两种语言进行 10,000 英尺的比较。
要继续学习,您至少需要对 JavaScript 及其怪癖有基本的了解。
您可以在浏览器控制台或 Node.js REPL 中尝试 JavaScript 代码示例。对于 Python,您可以使用 Python REPL。代码示例里出现 >>> 即表示 REPL。
享受阅读的乐趣!
Python 和 JavaScript 中的算术运算符
让我们从算术开始吧!
Python 和 JavaScript 或多或少共享相同的算术运算符。除了(无双关语)除法运算符之外,Python 还有一个整除运算符。
| 运算符 | PYTHON | JAVASCRIPT |
|---|---|---|
| 加法 | + | + |
| 减法 | - | - |
| 乘法 | | | |
| 指数 | | | |
| 除法 | / | / |
| 整除 | // | n/a |
| 取模 | % | % |
底除法返回两个数字相除的整数商。考虑以下 Python 示例:
>>> 89 / 4
# 输出:22.25
要获得整数而不是浮点数,我们可以使用向下除法(// 在 JavaScript 中是注释):
>>> 89 // 4
# 输出:22
在 JavaScript 中可用 Math.floor 得到同样结果:
Math.floor(89 / 4)
// 输出:22
Python 的算术运算符不仅仅对数字进行运算。例如,您可以使用字符串乘法来重复模式:
>>> "a" * 9
# 输出:'aaaaaaaaa'
或者加法也可以连接简单的字符串:
>>> "a" + "aa"
# 输出:'aaa'
对于其他一切,Python 都会引发 TypeError。这意味着你不能将数字和字符串相加:
>>> "a" + 9
# 类型错误:只能将 str (不是“int”)连接到 str
也不划分它们(除了没有意义之外):
>>> "a" / 9
# 类型错误:/ 不支持的操作数类型:“str”和“int”
在这方面,由于臭名昭著的类型强制,JavaScript 在外部观察者眼中完全是一团糟。不仅JavaScript的算术运算符可以自由地将数字转换为字符串:
"a" + 9
// 输出:“a9”
它们在无效算术运算的情况下不会引发任何错误:
"a" / 9
// 输出:NaN
相反,我们得到 NaN——由无效算术运算产生的特殊 JavaScript 值,难以处理,直到 ECMAScript 2015 Number.isNaN。
Python 和 JavaScript 中的自增和自减运算符
| 运算符 | PYTHON | JAVASCRIPT |
|---|---|---|
| 自增 | n/a | ++ |
| 自减 | n/a | -- |
JavaScript 有一个递增和一个递减运算符。两者都可以使用前缀(在数字之前):
var a = 34;
++a
或 后缀(在数字之后):
var a = 34;
一个++
在第一种情况(前缀)中,运算符递增数字并返回新数字:
var a = 34;
++a
// 返回 35
在第二种情况(后缀)中,运算符递增数字,但返回原始数字:
var a = 34;
一个++
// 返回 34
// 增加到 35
同样的规则也适用于减运算符。
相反,Python 中不存在递减/递增运算符这样的东西。相反,您必须使用赋值运算符。这是 Python 中的增量赋值:
>>> a = 34
>> a += 1
# a 现在 35
这是一个减量赋值:
>>> a = 35
>> a -= 2
# a 现在是 33
Python 和 JavaScript 中的比较运算符
JavaScript 和 Python 具有相同的比较运算符,除了 三等号,它象征着 JavaScript 奇怪的强制规则。
关于这个主题有大量文献,我不会过多打扰您。以下是并列的 Python 和 JavaScript 比较运算符:
| 运算符 | PYTHON | JAVASCRIPT |
|---|---|---|
| 大于 | > | > |
| 小于 | < | < |
| 大于等于 | >= | >= |
| 小于或等于 | <= | <= |
| 相等 | | | |
| 严格相等(三等号) | n/a | === |
| 不等于 | != | != |
| 严格不相等 | n/a | !== |
准确地说,Python 还有 is 运算符(此处未讨论),对我来说,它更多地属于身份运算符家族。
这里重要的是 Python 在比较值时是可预测的:
>>> 9 == "9"
# 输出:假
另一方面,每次涉及抽象比较运算符时,JavaScript 都会执行一次转换:
9 == "9"
// 输出:真
这里第一个运算符 9 在比较之前被转换为字符串。为了避免转换,您必须使用“严格比较运算符”,也称为三等于:
9 === "9"
// 输出:假
Python 和 JavaScript 中的逻辑运算符
现在我们讨论了算术和比较运算符,让我们看看它们的同伴:逻辑运算符。哦,我的朋友,我还记得高中时的布尔代数。你?
以下是 Python 和 JavaScript 中最常见的逻辑运算符:
| 运算符 | PYTHON | JAVASCRIPT | ||
|---|---|---|---|---|
| 逻辑与 | and | && | ||
| 逻辑或 | or | |||
| 逻辑非 | not | ! |
当您想要根据表达式的结果在代码中执行(或不执行)某些操作时,逻辑运算符非常有用。假设我们只想在 2019 年大于 1900 时打印“I am Cool”。以下是在 Python 中使用 逻辑运算符 and 执行此操作的方法:
>>> 2019 > 1900 and print("I am cool")
# 输出:“我很酷”
和表示:仅当左侧的内容为true时才执行右侧的内容。 JavaScript 中的相同逻辑映射为:
2019 > 1900 && console.log("I am cool")
或逻辑运算符的工作方式相反。 或表示:仅当左侧的内容为假时才执行右侧的内容。下面是一个 Python 示例:
>>> 2019 > 1900 or print("I am cool")
# 输出:真
结果是True
因为2019年大于1900所以评估就到此为止。 JavaScript 中的逻辑如下:
2019 > 1900 || console.log("I am cool")
最后但并非最不重要的一点是逻辑否定,当您想要翻转表达式的结果时很有用。例如,即使左侧给定的表达式为 false,我们也可以打印“I am Cool”。如何?使用 Python 中的 not 运算符:
>>> not 2019 > 151900 and print("I am cool")
# 输出:“我很酷”
2019 显然小于 151900,但 逻辑非 翻转了结果,因此仍然评估正确的表达式。下面的例子应该更容易理解:
>>> not False and print("I am cool")
# 输出:“我很酷”
JavaScript 中的逻辑否定使用感叹号:
!false && console.log("I am cool")
Python 和 JavaScript 中的基本数据类型
在计算机编程中,数据类型是您可以处理的“形状”。 大多数编程语言都有一组基本数据类型,如字符串、数字和布尔值。
Python 和 JavaScript 也不例外。以下概述了两种语言中可用的最重要的基本数据类型(n/a 代表不适用)。
| PYTHON | JAVASCRIPT | Python 中是否可变 | JavaScript 中是否可变 |
|---|---|---|---|
| float | Number | 否 | 否 |
| int | BigInt | 否 | 否 |
| int | n/a | 否 | n/a |
| string | String | 否 | 否 |
| boolean | Boolean | 否 | 否 |
| None | Null | 否 | 否 |
| n/a | Undefined | n/a | 否 |
| n/a | Symbol | n/a | 否 |
JavaScript 有八种类型,其中七种称为原语(对象本身就是一种类型)。从表中您可以注意到,Python 和 JavaScript 基本类型都是不可变的。例如,在 Python 中,我们将字符串称为 unicode 字符的不可变序列。
与大多数基本类型一样,Python 字符串也有常见操作的方法:
>>> name = "caty"
>> name.capitalize()
>> name.center(40)
>> name.count("t")
还有更多!
由于字符串是不可变的,因此字符串操作的结果始终是一个新字符串。
现在数字。在 JavaScript 中,整数和浮点数没有区别,它们只是一个 Number 数据类型。在 Python 中,有整数和浮点数。
JavaScript 最近获得了 BigInt,用于表示非常大的数字的原始类型。相反,Python 似乎使用单个 int 类型来处理小整数和大整数。
如果不提及典型的 boolean 那就太失职了,在 Python 中它可以假设 False 或 True。
关于特殊值的重要说明。Python 用 None 表示空值;JavaScript 中对应的是 null。
同样在 Python 中也没有 undefined 的概念;也没有与 JavaScript 的 Symbol 相当的类型(相当深奥的原语)。
Python 和 JavaScript 中的正则表达式
正则表达式是针对文本进行模式匹配的强大工具。为了在 Python 中使用正则表达式,我们使用 re 模块。
要创建新模式,我们可以使用 re.compile 编译正则表达式:
import re
regex = re.compile(r"\d\d\d\d")
re.compile 对于复杂的正则表达式特别有用,因为它可以加快速度。
有了正则表达式后,可用 .search() 在一段文本上匹配:
import re
regex = re.compile(r"\d\d\d\d")
text = "Your id is 4933"
match = regex.search(text)
这里我们用 \d\d\d\d 搜索文本中的四个连续数字。若匹配成功,结果是一个 match 对象,带有 .start() 和 .end() 两个方法。
这些方法分别返回已找到模式的文本部分的开始和结束索引:
import re
regex = re.compile(r"\d\d\d\d")
text = "Your id is 4933"
match = regex.search(text)
start, end = match.start(), match.end()
# 开始是 11,结束是 15
有了这些索引,我们现在可以提取匹配项:
import re
regex = re.compile(r"\d\d\d\d")
text = "Your id is 4933"
match = regex.search(text)
start, end = match.start(), match.end()
found = text[start:end] # 4933
在 JavaScript 中可用字面量或 RegExp 构造函数达到同样效果:
const regex = new RegExp(/\d\d\d\d/);
我们还可以将标志传递给构造函数。这里我们构建一个全局正则表达式:
const regex = new RegExp(/\d\d\d\d/, "g");
现在给定要匹配的文本,可用 .exec() 与 lastIndex 在字符串中多次匹配,或更好的是使用新的 String.prototype.matchAll(),返回 迭代器:
const regex = new RegExp(/\d\d\d\d/, "g");
const text = "Your 7795 id is 4933";
const match = text.matchAll(regex);
const found = [...match].map((el) => el[0]);
// ['7795','4933']
Python 数据类型和 JavaScript:不变性和变量
您可能从上表中注意到,大多数基本 Python 数据类型都是不可变的。 JavaScript在实践中似乎更轻松。在浏览器控制台或 Node.js REPL 中尝试以下示例。数字? 看起来可变:
var a = 34;
++a
// 返回 35
布尔值? 看起来也是可变的:
var x = false;
x++;
// 返回 0 并且 x 变为 1
无效的?再次,似乎是可变的:
var x = null;
x++;
// 返回 0 并且 x 变为 1
别被愚弄了。这里改变的不是底层原语,而是分配给变量的值。
最后两个带有 boolean 和 null 的示例是类型强制的情况。引擎将这两个值转换为相应的数字表示,并且变量被分配新值。
参见 this example on MDN for more。
Python 和 JavaScript 中的复杂数据类型
复杂数据类型是一种更复杂的形状,与字符串和数字等简单基元相反。在Python中,我们可以命名列表、字典、集合和元组。
相反,在 JavaScript 中,区别稍微不太明显,因为 Object
是最顶层的复杂类型和其他子类型,例如 Array
和Set
是其“祖先”的特殊版本(Function
也是一个对象)。
以下是 Python 和 JavaScript 中复杂数据类型的细分:
| PYTHON | JAVASCRIPT | Python 中是否可变 | JavaScript 中是否可变 |
|---|---|---|---|
| list | Array | 是 | 是 |
| dictionary | Object | 是 | 是 |
| set | Set | 是 | 是 |
| tuple | n/a | 否 | n/a |
正如你所看到的 JavaScript 中的数组和对象总是可变的,所以 Object
。除非使用外部库,否则无法真正保护对象(Object.freeze
是浅的)。不过,未来有计划将 immutable types 添加到 JavaScript 中。
另一方面,Python 是一种不可变的复杂类型,称为元组(而列表、字典和集合是可变的)。让我们更详细地了解 Python 复杂类型。
Python 数据类型:列表和元组
Python 中的列表是元素的集合,就像 JavaScript 数组一样。这是一个 Python 列表:
>>> my_list = ["vale", 98, "caty", None]
列表操作总是会改变原始列表:
>>> my_list = ["a", None, 44, True, "f"]
>> my_list.append(44)
>> my_list.remove('f')
>> print(my_list)
# 输出:[“a”,无,44,真,44]
相反,元组是不可变的,实际上它只有两个只读操作(索引和计数):
>>> my_tuple = ("vale", "Italy", 105)
>> my_tuple.count("Italy") # 1
>> my_tuple.index(105) # 2
请注意,元组包含在圆括号中。
我真的很喜欢 Python 如何使用加法运算符将两个列表连接在一起:
>>> my_list = ["a", None, 44, True, "f"]
>> another_list = ["b", None, 89, False, "x"]
>> my_list + another_list
# 输出:[“a”,无,44,True,44,“b”,无,89,False,“x”]
但是不敢用 JavaScript 尝试这个技巧,你会感到惊讶:
var my_list = ["a", null, 44, true, "f"]
var another_list = ["b", null, 89, false, "x"]
result = my_list + another_list
// 结果是:“a,,44,true,fb,,89,false,x”
与 JavaScript spec goes 一样,当操作数都是非字符串时(如本示例所示),加法运算符会将其操作数转换为字符串。注意,Python 列表和元组不能连接在一起:
>>> my_tuple = ("vale", "Italy", 105)
>> my_list = ["a", None, 44, True, "f"]
>> my_tuple + my_list
# 输出类型错误:只能将元组(不是“列表”)连接到元组
Python 数据类型:字典和 JavaScript 对象
JavaScript 的对象是键/值对的容器,也是其他专用对象的支柱:
var obj = {
姓名:“约翰”,
年龄:33
};
“JavaScript 中几乎所有东西都是对象”不仅仅是一种说法。这是事实。数组、函数、集合等等都只是 JavaScript 对象。
Python 有类似的东西,称为字典(或简称 dict),但它只是一个容器,而不是基本构建块:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
字典中的值可以通过它们的键访问或更改:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
>> my_dict["name"]
>> my_dict["city"] = "Florence"
Python 引发 KeyError
当您访问不存在的密钥时:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
>> my_dict["not_here"]
# 输出:KeyError:'not_here'
.get()
方法是直接键访问的更安全的替代方法,因为它不会引发,并且让我们为不存在的键指定默认值:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
>> my_dict.get("not_here", "not found")
# 输出:“未找到”
其他只读操作为.items()
,或 .keys()
:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
>> my_dict.keys() # ['name', 'city', 'age']
您还可以使用 .clear() 从字典中删除元素或完全清除它
:
>>> my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
>> my_dict.pop("name") # returns 'John' and removes it
>> my_dict.clear() # my_dict is empty now
列表、元组、字典和集合是集合或序列,因为它们保存一定数量的元素,并公开与它们交互的通用操作。
没有开关,没有派对
“Python 中没有开关?我应该怎么做”……几年前你就可以听到我的声音。你没看错没有 switch
在Python中。 Java 程序员喜欢它,JavaScript 开发人员也喜欢它。
switch
语句是 JavaScript 中的常见习惯用法。考虑以下示例:
function getUrlConf(host) {
switch (host) {
案例“www.example-a.dev”:
return "firstApp.urls";
案例“www-example-b.dev”:
return "secondApp.urls";
案例“www.example-c.dev”:
return "thirdApp.urls";
默认:
return "Sorry, no match";
}
}
getUrlConf("www-example-b.dev");
函数必须检查主机字符串以根据主机值返回相应的配置。配switch
我们比较值switch (host)
在 case 中
条款case "www.example-a.dev":
。对于每个 case
我们返回一个值。 default
子句确保在没有匹配项的情况下返回默认值。
Python 中没有 switch
:使用字典可以得到相同的结果:
def get_url_conf(host):
mapping = {
"www.example-a.dev": "firstApp.urls",
"www-example-b.dev": "secondApp.urls",
"www.example-c.dev": "thirdApp.urls"
}
return mapping.get(host, "Sorry, no match")
如果你问我的话,就干净多了。如果您更喜欢的话,还有 same pattern works for JavaScript。
JavaScript 对象传播/合并,Python 字典传播/合并
ECMAScript 2018 添加了“分解”JavaScript 对象的功能,也称为 “传播”。此语法对于保持对象不变(或克隆它们)特别方便:
const initial = {
不要触摸:“我的布雷尔”
};
const next = { ...initial, dontTouch: "just a copy" };
// initial.dontTouch 是“我的 breil”
// next.dontTouch 是“只是一个副本”
或者将它们合并在一起:
const a = {
姓名:“朱莉安娜”,
年龄:33
};
const b = {
姓氏:“克莱恩”,
城市:“旧金山”
};
const movie = {
书名:《高堡里的男人》
};
const all = { ...a, ...b, ...movie };
console.log(all);
/*
输出:
{
名称:“朱莉安娜”,
年龄:33,
姓氏:“克莱恩”,
城市:“旧金山”,
书名:《高堡里的人》
}
*/
在对象传播之前,使用 Object.assign 实现了相同的结果
:
const initial = {
不要触摸:“我的布雷尔”
};
const next = Object.assign({}, initial, { dontTouch: "just a copy" });
有几种方法可以在 Python 中获得相同的结果:一种方法涉及遍历 dict.update()
我不会在这里介绍。
字典解包看起来更像是 JavaScript 的对象传播,我更喜欢它。这是第一个例子:
initial = {"dont_touch": "my breil"}
next = {**initial, "dont_touch": "just a copy"}
这里是initial
被传播到 next
,“dont_touch”的值会覆盖原始值,同时保留 initial
。要合并两个或多个字典,我们可以这样做:
a = {"name": "Juliana", "age": 33}
b = {"surname": "Crain", "city": "San Francisco"}
movie = {"title": "The man in the high castle"}
all = {a, b, **movie}
不幸的是,字典解包有一些限制,导致 Python 添加字典的联合运算符。所以从 Python 3.9 开始你可以做 union:
a = {"name": "Juliana", "age": 33}
b = {"surname": "Crain", "city": "San Francisco"}
movie = {"title": "The man in the high castle"}
all = a | b | movie # Dict union!
print(all)
”“”
输出
{'name': 'Juliana', 'age': 33, 'surname': 'Crain', 'city': 'San Francisco', 'title': 'The man in the high castle'}
”“”
整洁的!更多info here。
Python 数据类型:Set 和 JavaScript Set
JavaScript 的 Set 是一种方便的数据结构,可用于存储唯一值。 Set 可以包含原始类型(甚至为 null 和未定义)或对对象的引用:
const mySet = new Set();
mySet.add( "aString" )
mySet.add( null )
mySet.add( NaN )
mySet.add(1)
mySet.add(1)
// Output: Set { 'aString', null, NaN, 1 }
除了添加元素、检查它们是否存在或循环遍历之外,您对 JavaScript Set 没有什么可以做的。 Set 有以下方法:
- .添加
- .清除
- .删除
- .条目
- .forEach
- .有
- .keys
- .尺寸
- .values
Python 的 Set 的工作原理大致相同。您可以在其中添加独特元素或也检查它们:
>>> my_set = set()
>> my_set.add( "aString" )
>> my_set.add( None )
>> my_set.add( 1 )
>> my_set.add( 2 )
>> my_set.add( 1 )
>> my_set
# Output: {None, 2, 'aString', 1}
然而,与 JavaScript 的主要区别在于 Python 有更多有用的数学集合运算方法,如“差”、“交”、“issubset”、“issuperset”、“union”等。
在 Python 中声明 Set 也有文字形式:
>>> my_set = { None, "caty", "venice", 84}
>> another_set = { "and", "mouse", 44, "a" }
正如您可能已经猜到的那样,两种语言中的设置对于删除重复项确实很有用。在Python中可以做一些不错的事情,比如union:
>>> my_set | another_set
# Output: {'and', 'mouse', None, 'venice', 'caty', 44, 84, 'a'}
或交叉点:
>>> my_set = { None, "caty", "venice", 84, "common_element" }
>> another_set = { "and", "mouse", 44, "a", "common_element" }
>> my_set & another_set
# Output: {'common_element'}
值得一提的是,集合在 JavaScript 和 Python 中都是可变的,但集合内的单个元素仍然是不可变的。
Python 和 JavaScript 中的成员资格运算符
“这个元素是否存在于另一个元素内部”? 会员运营商知道答案。 Python 和 JavaScript 都有一个名为 in 的成员运算符:
| PYTHON | JAVASCRIPT |
|---|---|
in | in |
在 Python 中,您可以对几乎任何数据类型使用 in。例如,您可以检查字符是否出现在字符串中:
>>> "o" in "Florence"
# 输出:真
它也适用于列表、元组和字典:
>>> my_list = ["vale", 98, "caty", None]
>> 98 in my_list # Output: True
>> my_tuple = ("vale", "Italy", 105)
>> "italy" in my_tuple # Output: False
my_dict = {
“姓名”:“约翰”,
“城市”:“罗马”,
“年龄”:44
}
my_dict 中的“age” # 输出:True
相反,在 JavaScript 中,in 运算符无论是在字符串上还是在数组上都不能直观地工作:
"a" in ["a", "b", "c"]
// 输出:假
“佛罗伦萨”中的“o”
// 输出:类型错误:无法使用“in”运算符在佛罗伦萨搜索“o”
它对字符串没有用,并且不搜索数组中的实际元素。它寻找的是数组索引:
1 in ["a", "b", "c"]
// 输出:真
考虑到 Array 是 Object 的子类型,并且将其想象为具有以下形状的对象更正确,这是有道理的:
var myArr = {
0:“一”,
1:“b”,
2:“c”
};
回顾一下,JavaScript 中的 in 仅适用于对象键,如果给定键存在于对象中,则返回 true:
var my_obj = {"name": "John", "city": "Rome", "age": 44}
my_obj 中的“名称”
// 输出:真
实例操作符:免责声明
“谁创建了这个对象”? 实例操作员尝试回答这个问题。现在,在你对我大喊大叫之前,让我先澄清一下。 下表只是我对一个更复杂的故事的心理表征:
| PYTHON | JAVASCRIPT |
|---|---|
isinstance() | instanceof |
type() | typeof |
正如您将看到的,在这个问题上不可能将 Python 和 JavaScript 1 对1 映射,因为 Python 是真正面向对象的,而 JavaScript 是基于原型的。
如果您了解一点 JavaScript,您就会知道“类”是一种幻觉,每次我们谈论 JavaScript 中的类时,我们都在撒谎。
但让我们用一些例子来揭开我的表格的神秘面纱。
Python 类、isinstance 和 JavaScript instanceof
考虑一个 Python 类,并由它创建一个新对象:
class Person:
def init(self, name, age):
self.name = 姓名
自我年龄 = 年龄
def print_details(self):
details = f"Name: {self.name} - Age: {self.age}"
print(details)
tom = Person("Tom", 89)
tom.print_details()
汤姆是什么?在面向对象编程中,我们说它是一个类实例,即从类蓝图构造的新对象。 Python 中的 isinstance (它是一个函数,而不是一个运算符)如果一个对象似乎是由给定的类构建的,则返回 true。所以在我们的例子中:
>>> isinstance(tom, Person)
# 输出:真
那么用 JavaScript 来代替呢?这是同一个类:
class Person {
constructor(name, age) {
这个.name = 名称
this.age = 年龄
}
printDetails() {
const details = Name: ${this.name} - Age: ${this.age}
console.log(details)
}
}
const tom = new Person("Tom", 44)
汤姆现在怎么样了?让我们来看看:
console.log(tom instanceof Person)
// 输出:真
并非严格的「实例」:tom 只是通过原型链连到 Person.prototype 的普通对象,同时也连到 Object:
console.log(tom instanceof Object)
// 输出:真
因此 JavaScript 的 instanceof 在对象「看起来」由某类构造时返回 true,并不是因为严格意义上的实例,而只是因为 tom 通过原型链连到了 Person.prototype。
我尝试比较两种语言的实例运算符,这是我能想到的最好的结果。我认为 Python 的 isinstance() 和 JavaScript instanceof 相似只是因为它们的行为,但这仍然是一个危险的比较。
如果是这样,您可能会想,为什么要引入 isinstance() 呢?这导致使用下一节......
Python 中的 type() 和 JavaScript 中的 typeof
任何时候你想检查 JavaScript 中给定值的类型,typeof 都是你的朋友:
typeof "alex" // "string"
typeof 9 //“数字”
typeof [1,2] // “对象”
当我们想要检查函数是否存在时,JavaScript 中的 typeof 很方便,例如:
if (typeof window.futureStuff === "undefined") {
window.futureStuff = function () {
// 做事
}
}
来自 JavaScript 的您可能希望在 Python 中使用类似的运算符。 type() 函数可能是您的首选,因为它似乎与 JavaScript 的 typeof 相关。但是 type() 返回不同的东西:
>>> type(tom)
# 输出:
>> type('ahh')
# 输出:<类'str'>
不太方便。 isinstance() 非常适合这里的工作,这就是我在上一节中引入它的原因。它的工作原理如下:
>>> isinstance(9, int)
# 输出:真
>> isinstance(tom, Person)
# 输出:真
>> isinstance("caty", str)
# 输出:真
当您需要根据另一个已知类型检查值时 isinstance() 很有用。然而,可能存在您需要模仿 typeof 的情况。在这种情况下,您仍然可以通过根据内置类型检查对象来使用 Python 的类型函数:
>>> type(9) is int
# 输出:真
Python中如何处理异常:什么是异常?
如果程序能一直运行良好就好了。但现实世界有点疯狂。如果一个程序可能会失败,那么它肯定会失败。
考虑一个简单的 Python 函数。这里可能会出现什么问题?
def divide(a, b):
result = a / b
return result
divide(89, 6)
divide(89, "6")
divide(89, 2)
将代码保存在文件中(我将其命名为 exceptions.py
)并运行它。你会看到很多东西,更重要的是,一个TypeError
:
TypeError: unsupported operand type(s) for /: 'int' and 'str'
它来自对divide的第二次调用,我们试图将一个数字和一个字符串相除,这在Python中是无效的(在JavaScript中也是如此)。程序停止,永远不会到达第三个函数调用。
Python 在这里所做的是引发异常,即一个异常事件会导致我们的代码崩溃。
所以异常是严重的错误,大多数时候可以停止程序。我们如何处理异常决定了失败的程序和可以自行恢复的程序之间的区别。
处理 Python 和 JavaScript 中的同步异常
如果我们处于同步世界,那么在处理异常方面,Python 和 JavaScript 非常相似。
在Python中有try/except
。 try
块应该处理代码的“快乐路径”,而 except
将拦截实际的异常。
前面的示例可以重构为:
def divide(a, b):
try:
return a / b
except TypeError:
print("Oops!")
divide(89, 6)
divide(89, "6")
divide(89, 2)
请注意,您应该指定除 TypeError 之外的错误类型
在我们的例子中。
try/except
或多或少意味着:尝试此代码,如果失败拦截错误并执行其他操作。
现在程序打印“Oops!”,但是它不再停止。所有函数调用(除了出错的函数调用)都会继续运行。
您发现了如何在 Python 中处理异常! JavaScript 怎么样?
考虑以下示例 (JavaScript):
function find(member, target) {
return member in target;
}
find("a", "siena");
这是对in的无效使用
运算符(仅适用于对象)。
运行代码,您应该看到:
TypeError: Cannot use 'in' operator to search for 'a' in siena
好的 JavaScript,很公平。为了处理错误,JavaScript 中有 try/catch
。 try
尝试快乐的道路,而catch
处理问题。
我们可以将代码重写为:
function find(member, target) {
try {
return member in target;
} catch (err) {
console.log("Oops!");
}
}
find("a", "siena");
var result = find("city", { name: "Jane", city: "London" });
console.log(result);
现在程序打印“哎呀!”、“true”,并且不再停止。
注意参数err
?让我们在下一节中看看如何使用它。
嘘。查看 "A mostly complete guide to error handling in JavaScript" 以获取有关错误和异常的深入指南。
如何处理异常:在 Python 和 JavaScript 中使用错误消息
处理异常固然很棒,但打印“Oops”却并非如此。最好打印错误消息。
在JavaScript中,我们可以使用catch的参数
,大多数时候叫error
或err
作为一个惯例。它包含实际的异常对象。
反过来,几乎每个异常都有一个名为 message 的属性
,与实际的文本错误。前面的 JavaScript 示例变为:
function find(member, target) {
try {
return member in target;
} catch (err) {
console.log(err.message);
}
}
find("a", "siena");
var result = find("city", { name: "Jane", city: "London" });
console.log(result);
打印:
Cannot use 'in' operator to search for 'a' in siena
真的
比“哎呀”好多了。
在Python中,你可以用as做同样的事情
:
def divide(a, b):
try:
return a / b
except TypeError as err:
print(err)
这个例子不言而喻:拦截TypeError
并创建一个名为 err 的绑定
for convenience. Then print the error.
在 Python 和 JavaScript 中引发自己的异常
与编程中的几乎任何事物一样,即使对于最简单的事物,术语也是模糊的。
以术语抛出为例。当我们开发人员需要终止程序并说“呃,这不好,让我们停止”时,在 JavaScript 中通常会说“抛出”。
错误可能是 JavaScript 引擎抛出的,也可能是开发人员在检查无效值时故意抛出的。
我们也可以说“引发”而不是抛出,并将错误称为“异常”。你为什么会提出或抛出错误?再次考虑 JavaScript 示例。我们知道in
运算符仅适用于对象。
如果我们检查参数 target,当它是字符串时,我们可以 抛出 错误。方法如下:
function find(member, target) {
if (typeof target === "string")
throw TypeError("Target must be an object, got string");
try {
return member in target;
} catch (err) {
console.log(err.message);
}
}
现在调用该函数:
find("a", "siena");
添加 throw 的最终效果是该函数的使用者将得到错误:
Error: Target must be an object, got string
这是预期的,如果目标是字符串,我们希望停止执行,因此代码永远不会到达 try
堵塞。
现在让我们回到Python。我们留下了这段代码:
def divide(a, b):
try:
return a / b
except TypeError as err:
print(err)
这里我们可以在相除之前检查两个参数是否都是数字,然后抛出错误。 throw
Python 中的对应项是 raise
:
def divide(a, b):
if isinstance(a, str) or isinstance(b, str):
raise TypeError("预期,得到对象")
try:
return a / b
except TypeError as err:
print(err)
当调用该函数时:
divide(1, "2")
你应该看到:
TypeError: Expected in, got object
注意这次使用TypeError
for creating a custom error message. If you want you can also extend Exception
创建您自己的自定义异常:
class CustomError(Exception):
pass
def divide(a, b):
if isinstance(a, str) or isinstance(b, str):
raise CustomError("预期,得到对象")
try:
return a / b
except TypeError as err:
print(err)
divide(1, "2")
作为 raise 的替代品
我们也可以使用assert
,方便开发中的调试:
def divide(a, b):
断言 isinstance(a, str), "预期 int,得到对象"
断言 isinstance(a, str), "预期 int,得到对象"
try:
return a / b
except TypeError as err:
print(err)
divide(1, "2")
使用 reStructuredText 编写 Python 代码文档
在 JavaScript 中,我们使用 JSDoc 来添加类、函数参数、函数返回值等的文档。下面是 JSDoc 带注释的函数的示例:
/**
将数字提高到指数
@param {number} value - 要提高的基数
@param {number} exponent - 指数
@return {number} - 指数幂
*/
function poooow(value, exponent) {
return value exponent;
}
请注意参数和返回上的“number”类型。通过添加这些类型提示,您可以帮助 IDE 在您使用该函数时为您提供帮助(无双关语)。同样的概念也适用于 Python。
如何在Python中添加代码文档?实际上有很多方法,但reStructuredText是推荐的方法之一。 reStructuredText 是纯文本,可以嵌入到 Python 文档字符串中。
Docstring 只是一个恰好存在于 Python 函数或模块内部的字符串,并充当代码的文档。为了给您提供一些背景信息,这里有一个 Python 函数内的 Docstring 示例:
def string_repeat(string, count):
”“”
重复字符串 N 次
”“”
return string * count
当您决定添加参数和返回值的文档时,reStructuredText 就会发挥作用。这是我们的示例,使用 reStructuredText 进行了丰富:
def string_repeat(string, count):
”“”
重复字符串 N 次
:param string: 要重复的字符串
:param count: 重复次数
:return: 字符串重复N次
”“”
return string * count
我知道,文档可能看起来多余,但如果您决定尝试静态类型,reStructuredText 可以大放异彩。首先,有一点类型理论。
一点类型理论:动态类型与静态类型
Python 和 JavaScript 是动态语言。也就是说,它们给予程序员几乎绝对的自由。例如,在 JavaScript 中,变量可能首先包含字符串,然后更改为包含布尔值。 JavaScript 引擎不会抱怨。 (但是,您可以使用 const 防止重新分配基本类型)。
Python 也做同样的事情。考虑以下示例:
>>> name = "Caty"
>> name = True
>> print(name)
真的
变量 name 最初是字符串类型,但后来更改为布尔类型。大多数时候通过自律,你可以驯服动态打字并在晚上睡觉。在某些情况下,动态类型可能是不允许的,或者风险太大(想想金融系统)。
与动态类型相反,静态类型意味着为每个变量分配一个固定类型,因此变得更难以意外更改类型。
然而,静态类型是一种不同的野兽,学习它可能是一个痛苦的过程,特别是对于初学者来说。
幸运的是,在 JavaScript 和 Python 中,我们都可以使用文档来“逐步打字”。
让我们在下一节中了解如何在 Python 中使用 reStructuredText 添加类型。
使用 reStructuredText 在 Python 中逐步输入
借助 reStructuredText,您可以熟悉 Python 中的静态类型,而无需在代码中添加实际类型。
除了“:param”和“:return”等常见标签之外,reStructuredText还有:type和:rtype,分别用于为参数和返回值添加类型。
如果您想跟随代码进行操作,那么现在是创建并激活 Python 虚拟环境的好时机(在下一节中,我们还需要安装 mypy):
mkdir docs_and_typings && cd $_
python3 -m venv venv
源 venv/bin/activate
为了说明该示例,请在项目文件夹中创建一个名为 my_str_utils.py 的新文件,其中包含以下函数:
def string_repeat(string, count):
”“”
重复字符串 N 次
:param string: 要重复的字符串
:param count: 重复次数
:return: 字符串重复N次
”“”
return string * count
现在让我们使用 :type 和 :rtype 在 reStructuredText 中添加 类型:
def string_repeat(string, count):
”“”
重复字符串 N 次
:param string: 要重复的字符串
:类型字符串:str
:param count: 重复次数
:类型计数:int
:return: 字符串重复N次
:r 类型:str
”“”
return string * count
这是渐进式打字:您现在拥有一个类型化函数,而没有静态类型系统的认知负担。 大多数 IDE 都能够读取 reStructuredText 类型,例如在 PyCharm 中 当参数的类型与实际参数的类型不匹配时,您会收到警告:
同样在 Pycharm 中,通过单击函数名称并按 Ctrl+Q(或 MacO 上的 F1),您可以获得实际的类型提示:
这些提示与我们将在下一节中看到的“真实类型提示”相同(大多数时候,即使没有 :rtype,PyCharm 也能够通过检查 return 语句来推断返回类型)。
在 JavaScript 中,我们可以使用 JSDoc types 获得相同的效果。
Python 和 JavaScript 中的静态类型
我之前给出的渐进打字的定义有点模糊。确实,您可以通过文档字符串中的代码文档了解静态类型。
但是渐进类型的更严格解释是:在代码中逐渐添加真实类型的过程。现在我所说的“类型”是指可以使用工具检查的类型注释。
JavaScript 有 TypeScript:JavaScript 之上的一层,您可以插入其中。 TypeScript is an actual language on its own,但可以逐渐适应任何 JavaScript 代码库。
Python 也是如此:近年来它获得了 an optional type system based on "type hints"。在这里,我们将在 30,000 英尺的高度看到 Python 类型提示。 一旦掌握了基础知识希望您将能够将类型应用到您的Python代码中。让我们采用上一节中的 string_repeat :
def string_repeat(string, count):
”“”
重复字符串 N 次
:param string: 要重复的字符串
:类型字符串:str
:param count: 重复次数
:类型计数:int
:return: 字符串重复N次
:r 类型:str
”“”
return string * count
这次我们将去掉 reStructuredText 类型:
def string_repeat(string, count):
”“”
重复字符串 N 次
:param string: 要重复的字符串
:param count: 重复次数
:return: 字符串重复N次
”“”
return string * count
此时 IDE 将失去所有提示。但是如果您还记得上一节中的“真实”类型提示就像“参数:类型”,那么将它们添加到函数签名中应该很容易:
def string_repeat(string: str, count: int):
# 忽略
该样式与 TypeScript 使用的样式类似。您还可以使用 -> type 键入返回值:
def string_repeat(string: str, count: int) -> str:
”“”
重复字符串 N 次
:param string: 要重复的字符串
:param count: 重复次数
:return: 字符串重复N次
”“”
return string * count
对代码进行类型注释后,您可以使用名为 mypy 的工具检查其正确性,这是我们下一节的主题。
使用 mypy 检查类型
mypy 是一个 Python 类型检查器。要在测试项目中安装 mypy,请确保激活虚拟环境,然后运行:
pip install mypy
打开 my_str_utils.py 并尝试将 string_repeat 与 float 而不是 int 一起使用:
def string_repeat(string: str, count: int) -> str:
”“”
重复字符串 N 次
:param string: 要重复的字符串
:param count: 重复次数
:return: 字符串重复N次
”“”
return string * count
string_repeat('hello', 15.6)
IDE 会警告您,但您也可以对 Python 脚本使用 mypy:
mypy my_str_utils.py
mypy 也会警告你:
my_str_utils.py:11: error: Argument 2 to "string_repeat" has incompatible type "float"; expected "int"
在 1 个文件中发现 1 个错误(检查了 1 个源文件
与编译为“普通”JavaScript 的 TypeScript 不同,mypy 不会生成任何代码,它只是检查其类型。
面向 JavaScript 开发人员的 Python:更进一步
您可能感兴趣的其他文章:
感谢您的阅读并继续关注此博客!