快速上手 Python
像 JavaScript 这类语言的设计,适合新手学习理解特别简单编程概念。比如函数。但如果要理解面向对象(Object Oriented)的概念,用 JavaScript 会比较绕。因为它设计比较奇怪,你得去理解一些专门针对 JavaScript 设计的东西。
相比之下,用 Python 来理解面向对象,思路会清晰很多。而且 Python 也有好的 IDE 支持(PyCharm 挺不错的)。当前 Python 特别流行,也是有一定原因的。
Python 编程环境
一般推荐两个编辑器,或者说 IDE ,来快速实践各种代码:
- VS Code:微软开发的一款超轻量 IDE ,Teahour 91期 有介绍,使用方法参考 快速上手 VS Code
- PyCharm:JetBrains 团队开发的一款 Python 的 IDE ,安装和使用方法参考这个 YouTube 视频
此外,还可以选择 安装配置 Anaconda ,以使用 Anaconda 附带的 Python 在终端里运行。
## 通过 Homebrew 安装 python
$ brew install python3
## 查看 Python 的版本
$ python3 --version
Python 3.6.5 :: Anaconda custom (64-bit)
## 运行 *.py 的 Python 脚本文件
$ cat ~/scripts/first_script.py # 显示 first_script.py 文件里的内容
print('Congratulations on running this script!!')
$ python3 ~/scripts/first_script.py # 用 python3 运行 first_script.py 文件
Congratulations on running this script!!
## 直接输入命令 python ,不带任何文件和参数,会进入 interactive interpreter(交互式解释器),可进行各种 Python 的语法测试
$ python3
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>exit()
$PS:
个人推荐新手使用 VS Code ,这东西介于编辑器和 IDE(Integrated Development Environment)之间,既有编辑器的简洁,又有 IDE 的强大功能,关键是安装配置起来特别简单,最适合新手。
可以参考这篇上手 Visual Studio Code 设置 Python 的运行环境。
Python 的一些基本规则
- 变量区分大小写,即 case sensitive
- 空格重要,附带语意,即 spacing matters
- 运算符号有优先级(比如乘除法 * / 优先级高于加减法 +-)
- 运算的优先级可以使用括号来确定:优先级方向为从里往外
- Python 中名为
_的变量(underscore variable)多数情况下代表忽略这个值,其他详细参见这里 - Python 变量名的规则不用 listSum 这种大小写混合,而是用下划线分隔多个单词,写成 list_sum 这样
- Python 赋值时是「传址(引用)」效果,而不是「传值」效果。见下例:
a = [1, 2, 3]
b = a # b 和 a 指向同一个列表对象,而不是把 a 的内容复制给 b
b.append(4) # 修改 b 的内容,a 也会一起改变
print(a) # 输出 [1, 2, 3, 4]
# 如果函数参数有默认值,这个值又是可变对象时,容易出错
def foo(data=[]):
data.append(1)
return data
print(foo()) # [1]
print(foo()) # [1, 1] - 意外结果基本数据类型
整数 - int
>>> 2 * 3 6 >>> 3 // 2 # 取整 1浮点数 - float
整数和浮点数在电脑里是有差别的,整数和浮点数混合计算会被转化为浮点数,结果也是浮点数>>> 3 / 2 1.5 >>> 1.5 * 2 3.0复数 - complex
这是一种用于数学计算的数据类型,Python 中用j来表示虚数>>> (1 + 2j) * (1 + 2j) (-3+4j)字符串 - string
可以用单引号'hello',也可以用双引号"hello"
一个常用的字符串方法:str.split()>>> '1,2,3'.split(',') ['1', '2', '3'] >>> '1,2,3'.split(',', maxsplit=1) ['1', '2,3'] >>> "1,2,,3,".split(',') ['1', '2', '', '3', '']布尔类型 - bool
Python 中不用&&而是用and来代表「且」
其实&&并不是一个好设计,因为这个符号没有and直观易懂>>> 1<2 and 3>4 False >>> 1<2 or 3>4 True >>> not 1<2 False >>> 1 == 2 False >>> type(1==2) <class 'bool'> # 注意这里返回的类型,不是「字符串」 >>> type(type(1==2)) <class 'type'> # 它是一个 type 类,即 class 'type' >>> type(1==2) == "bool" # 所以不能用这种方式来判断数据的类型 False >>> type(1==2) == type(1<2) # 对应地,可以用这种方式来判断数据类型 True >>> type(1==2) == type(False) # Python 的类型设计是要比 JavaScript 严格很多的 True # 所以做科学计算或者 AI 用的就是 Python 而不是 JavaScript ## 正规判断类型的方法是用 isinstance(object, type) >>> isinstance(2, bool) False >>> isinstance(2 < 3, bool) TrueNone
这是 Python 里面的一种专门的数据类型,代表「没有」,类似 JavaScript 里的 Null>>> None # 注意第一个字母 N 是大写 >>> x = None >>> x # 没输出不显示 >>> print(x) # 用 print 能打印出来 None >>> print(None) None
基本的数据结构
列表 - List
在编程中,「链表(Linked List)」和「列表(List)」通常不是同一个概念:
链表是一种数据结构,由一系列节点组成,每个节点包含数据以及指向下一个节点的指针;
列表是一种抽象数据类型(ADT),用来存储一系列元素的有序集合。列表可以有不同的实现方式,包括数组、链表等,甚至可以是其他数据结构的组合。链表是列表的一种实现方式;
在编程中,「列表」通常更广泛地指代一种抽象数据类型,而「链表」则更具体地指代一种特定的数据结构。
因此,它们的主要区别在于抽象性和具体性;>>> li = ['1', '2', '3'] ## li 的定义看起来像数组,但其实不是,它的类型是 class 'list' ## 但你可以认为它就是一个数组 >>> type(li) <class 'list'> ## 所以你可以用下标访问 - 下标从 0 开始 >>> li[0] 1 ## slice 操作 ## 访问 list 中下标从 0 到 2 的部分 - 注意这里不包括 li[2] ## 相当于 [0, 2) 的感觉 >>> li[0:2] [1, 2] ## 访问从头到某个下标(不包括 li[1]) >>> li[:1] [1] ## 访问从某个下标到最后 >>> li[1:] [2, 3] ## 赋值或者定义的时候,传递的是引用,而不是复制一份 >>> li [1, 2, 3] >>> li2 = li >>> li2[1] = 9 # 可以看到 li2 改变的同时,li 也被改变了 >>> li [1, 9, 3] ## 如果要复制一份而不是传递引用,需要用 .copy() 这个方法 >>> li2 = li.copy() >>> li[1] = 2 >>> li2 [1, 9, 3] >>> li [1, 2, 3] # 所以在使用 python 的时候你要注意它是同一个对象还是不同的 ## 越界访问会报错 - 这是个好设计 ## 如果返回 undefined 之类的数据,就有可能被到处传递引发潜在问题而难以被定位纠正 >>> li[3] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range ## 计算列表长度 >>> len(li) 3 ## 使用 dir 可以看到一个类型(对象)里有哪些方法(methods) >>> dir(li) ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] ## 按 2 次 Tab 键进行补全,它就会提示你当前可用的所有方法(methods) >>> li. li.append( li.copy( li.extend( li.insert( li.remove( li.sort( li.clear( li.count( li.index( li.pop( li.reverse( ## 插入元素 - 这里 insert 元素到开头还是比较费事的,不像 pair 那种 list 时间复杂度只有 O(1) ## 为了程序的效率,一般不会把东西插到中间 >>> li.insert(0, 9) >>> li [9, 1, 2, 3] ## 排序 >>> li.sort() >>> li [1, 2, 3, 9] ## 取出最后一个元素 >>> li.pop() 9 >>> li [1, 2, 3] ## 列表里增加新元素(element) ## 效果一 >>> li.append([4]) >>> li ['1', '2', '3', [4]] # 可以看到 list 里每个元素的类型不一定要相同 ## 效果二 >>> li.extend([4]) >>> li ['1', '2', '3', [4], 4] ## 效果三 >>> li + [5] ['1', '2', '3', [4], 4, 5] >>> li ['1', '2', '3', [4], 4] ## 使用「列表推导式(List Comprehension)」来生成 List >>> li_c = [x**2 for x in range(10)] >>> li_c [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 「列表推导式」中的 if >>> [x for x in li_c if x % 2 == 0] [0, 4, 16, 36, 64] ## 「列表推导式」中的 if-else ## 注意这里用了跟上面单纯的 if 不同的结构,所以 if 和 for 的位置顺序也不一样 ## The expression 「 x if C else y 」 first evaluates the condition, C rather than x. ## If C is true, x is evaluated and its value is returned. ## Otherwise, y is evaluated and its value is returned. >>> [x if x % 2 == 0 else 3 for x in li_c] [0, 3, 4, 3, 16, 3, 36, 3, 64, 3] ## 「列表推导式」中的「列表推导式」- 嵌套 >>> [ [x**2 for x in li] for li in [[1,3,5], [2,4,6]] ] [[1, 9, 25], [4, 16, 36]] ## 对 List 中的元素进行 map 操作 >>> list(map(lambda x:x+1, li_c)) [1, 2, 5, 10, 17, 26, 37, 50, 65, 82]元组 - tuple
tuple和list的区别在于,tuple是不可更改的## Tuple 用的是括号来表示,区别于 list 的方括号 [] >>> t = (1, 2, 3, 4, 5, 5, 5) >>> t[1] 2 ## 无法更改 >>> t[1] = 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment ## len 函数可以接收多种数据类型 - 这里是一个面向对象语言好的地方 >>> len(t) 7字典 - dict
字典本质上就是哈希表,即键值对(key-value pair)。比如对于{'Ace': 1, 'Queen': 12}这一字典来说,‘Ace’ 和 ‘Queen’ 就是键(Key),而对应的 1 和 12 就是值(Value)## dict 用花括号来表示,类型是 class 'dict' >>> d = {'a': 1, 'b': 2} >>> d {'a': 1, 'b': 2} >>> type(d) <class 'dict'> ## 你需要用下标来访问,而不能当成对象(object)来用 ## Python 里把 dict 和 object 区分开了 - 这是一个好设计 >>> d['a'] 1 >>> d.a # 用 .a 来访问会报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'dict' object has no attribute 'a' ## 修改和添加 >>> d['a'] = 5 # 赋值 >>> d['c'] = 12 # 添加新的 key >>> d {'a': 5, 'b': 2, 'c': 12} ## 判断某个 key 是否存在与某个字典中(注:case sensitive) >>> "ace" in {'ace': 1} True ## 计算字典长度 >>> state_value = {(6, 10, False): -1.0, (15, 10, False): -1.0} >>> len(state_value) 2 ## 使用「列表推导式(List Comprehension)」来生成字典(dictionary) >>> vocab_to_int = {word: ii for ii, word in int_to_vocab.items()} ## 这里也可以用「列表推导式(List Comprehension)」来实现使用函数 f 来对字典的内容进行 Map {k: f(v) for k, v in my_dictionary.items()}模组 collections 中的 defaultdict 类能更方便地处理 value 为列表(list)、字典(dictionary)的情况,其用法参见Using defaultdict in Python
>>> from collections import defaultdict >>> city_list = [('TX','Austin'), ('TX','Houston'), ('NY','Albany'), ('NY', 'Syracuse'), ('NY', 'Buffalo'), ('NY', 'Rochester'), ('TX', 'Dallas'), ('CA','Sacramento'), ('CA', 'Palo Alto'), ('GA', 'Atlanta')] >>> >>> cities_by_state = defaultdict(list) >>> for state, city in city_list: ... cities_by_state[state].append(city) ... for state, cities in cities_by_state.iteritems(): ... print state, ', '.join(cities) ... NY Albany, Syracuse, Buffalo, Rochester CA Sacramento, Palo Alto GA Atlanta TX Austin, Houston, Dallas集合 - set
集合也是数学计算中会常用到的数据结构:## Set 也是花括号,可以注意到内部的顺序是乱的 >>> s = {'a', 'b', 'c', 't', 'hello'} >>> s {'t', 'b', 'hello', 'c', 'a'} ## 增加元素 >>> s.add('world') >>> s {'world', 't', 'b', 'hello', 'c', 'a'} ## 判断某个元素是否存在 - 可以认为这是个二元操作(binop) >>> 't' in s True >>> 'hi' in s False ## set 的相关运算 >>> s2 = {'a', 't', 'world', 'y', 'i'} >>> s - s2 {'b', 'hello', 'c'} >>> s {'world', 't', 'b', 'hello', 'c', 'a'} # 不会改变原来的集合 s >>> s.intersection(s2) {'world', 't', 'a'} # 交集 >>> s.union(s2) {'world', 't', 'b', 'c', 'i', 'y', 'hello', 'a'} # 并集
Python 中的注释
- 以
#开头的行 - 以
"""(三个双引号)包裹的代码块(Code Block)
## codes by chpwang - 2018-05-17 04:57:56
"""
任务3:
xxxxxx
"""变量定义
Python 里面定义变量不需要 var 这类关键词,直接写就行,末尾也不用分号。
## 变量定义
x = 2 * 3
## 赋值
x = 12
## 变量定义还可以解析结构
l1 = [9, 8]
[u, v] = l1
print(u) # 9
print(v) # 8函数
Python 里面经常用「冒号」来分隔语言的各个部分。这里参数后面是冒号。新手要注意,冒号容易忘记写。
## 匿名函数 x => x * x
lambda x: x * x
## 函数调用
(lambda x: x * x)(3)
## 函数定义
square1 = lambda x: x*x
def square2(x): return x*x
## 这里「缩进」很重要,python 靠缩进来判断函数体
## 如果函数体语句多,注意缩进要对齐,不能有的缩进 2 格,有的只缩进 1 格
## 「缩进」是一种危险的糟糕设计,容易出错,比如有时 return 缩进错了,可能就跑到外部去了
## 缩进设计似乎只是为了少写一些括号
def square3(x):
return x*x
result1 = 10
result2 = 10
## square4 中的 result 是局部变量,不改变外面的 result
def square4(x):
result1 = x*x
return result
## 加了 global 关键词后,square5 中的 result 就是全局变量了
## 调用 square5 会改变外面的 result
def square5(x):
global result
result = x*x
return result
print(square4(4)) # 16
print(result1) # 10
print(square5(4)) # 16
print(result1) # 16
## 函数也可以什么都不做,但要写一个 pass ,不能什么都不写
def do_nothing(x):
pass>>> def minus(x, y): return x - y
...
>>> minus(9, 6)
3
>>> minus(y=9, x=6) # python 的函数调用可以指定参数的值,这样可以无视参数的调用顺序
-3 # 好处是如果参数比较多,把各参数名和值写上,代码阅读起来就很直观 - 这是个好设计条件分支 - if 语句
注意,else if 分支在这里写作 elif
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
if season == 'spring':
print('plant the garden!')
elif season == 'summer':
print('water the garden!')
else:
print('unrecognized season')
## 三元操作符(Ternary Operator)写法
x = true_value if condition else false_value
## lambda 中的 if - 无法在 lambda 匿名函数中使用 print 打印,或者 raise 报错
lambda x: True if x % 2 == 0 else False循环
## Python 中的循环一般不用下标
>>> a = [2, 3, 4, 6]
>>> for x in a:
... print(x)
...
2
3
4
6
## 可以使用这种模式匹配来直接取出 key 和 value 而不用后续访问下标 - 这是好设计
>>> for [k, v] in [["dog", 1], ["cat", 2], ["fish", 3]]:
... print(f"{k}: {v}")
...
dog: 1
cat: 2
fish: 3
## 模式匹配(pattern match)还可以用于赋值,比如交换变量的数据
>>> [x, y] = [3, 4]
>>> x
3
>>> y
4
>>> [x, y] = [y, x]
>>> x
4
>>> y
3
## 和 range(start, stop, step) 函数结合使用
>>> for i in range(0, 2, 1):
... print(i)
...
0
1
## 这里说明一下这个 range
## 可以认为 range(5) 的效果就相当于生成了一个 [0, 1, 2, 3, 4] 这样的 list
## 然后我们就可以 for i in range(5)
## 虽然效果上是一样的,但实际上 range 不会真的生成一个 list
## range 多用于循环,如果真生成一个 list ,就会影响计算性能并占用空间
## 比如这样一来 range(1000000) 就很吃计算资源了
## range 实际上生成的是一种 lazy list(惰性列表)
## 惰性列表的意思是,它不会真的给你放到内存里面去
## range(5) 会返回一个 range 对象,按需计算元素的值,内存高效(memory efficient)
## python 中的 for 循环都要用 for x in ... 这种形式,如果 x 是是数字,就要用这个 range
## 有人认为这样的 for 循环表达能力有限,不能表达复杂的逻辑,range 只能表达简单的数字
## 比如无法写出类似 for(var i=0, j=array.length; i < j; i++, j--) 的代码
## 但其实这是 python 的一个好设计。当表达不了的时候,就不应该使用 for 循环,而要改用 while 循环
## 上述复杂的 for 循环需要思考到底是先判断 i < j 还是先计算 i++ 和 j--,不利于代码阅读理解
## 用 reversed 配合 range 倒数
## 写成 for i in range(4, -1, -1) 虽然是同样的效果,但阅读理解上不直观
>>> for i in reversed(range(0, 5)):
... print(i)
...
4
3
2
1
0
# 和 zip() 函数结合使用
>>> alist = ['a1', 'a2', 'a3']
>>> blist = ['b1', 'b2', 'b3']
>>> for a, b in zip(alist, blist):
... print(a, b)
...
a1 b1
a2 b2
a3 b3
## upack 一个 list 也是常用的循环 - 若觉得下行的说明不清楚就开终端试一试
## 此例第 1 次循环取出第一个元素 [1, 3],然后把第一项和第二项分别赋值给 a 和 b
>>> for a, b in [[1,3],[2,4]]:
... print(a)
... print(b)
...
1
3
2
4
## 使用 * (星号 star)和 zip 来操作 list
## 下划线符号:_ 在 Python 中代表占位符,相当于吃掉这两个位置的输出 - 下面的代码原本会输出 3 个结果,但只想要其中最后一个结果,所以使用下划线来抛弃前两个结果
>>> _, _, rewards = zip(*[((6, 10, False), 1, 0), ((17, 10, True), 1, 0), ((21, 10, True), 0, 1.0)])
>>> rewards
(0, 0, 1.0)
## while 循环
>>> while i < 6:
... print(i)
... if i==3:
... break
... i += 1
...
1
2
3类 - class
这里以实现查找表为例演示 class 的用法:
- python 的类里要有这个
__init__作为「构造函数」,如果没写 Python 也会给一个默认的:def __init__(self): pass - 类里面绑定到具体实例的函数,第一个参数通常都是
self,这是个自动接收的参数,代表类的当前实例对象 - @classmethod 和 @staticmethod 标记的函数
## Map 没有定义 __init__ ,但还是程序还是会给它一个默认的 __init__
class Map:
def print_data(self):
print("'Map' object has no attribute 'data'")
class Table:
def __init__(self):
self.data = []
def add(self, key, value):
self.data.insert(0, [key, value])
def lookup(self, key):
for [k, v] in self.data:
if k == key:
return v
return None
class Hash:
def __init__(self):
self.data = {}
def add(self, key, value):
self.data[key] = value
def lookup(self, key):
return self.data[key]输出
这里介绍的是常用的输出语法,更详细的介绍可以参考这篇 How to use str.format()
## 普通输出 - 使用 {} 占位,.format() 填充占位符
>>> print("{} ~ {}% dog wins.".format(3, 9))
3 ~ 9% dog wins.
## 保留 5 位小数
>>> print("{} - {:.5f}".format(3,9.0/3.5))
3 - 2.57143
## 保留 1 位小数
>>> print("{} - {:.1f}".format(3,9.0/3.5))
3 - 2.6在 Python 中,还可以使用 f-string 这种种字符串格式化的方法来输出。只需在字符串前加上字母 f 或 F ,然后在字符串中使用花括号 {} 包裹要插入的变量或表达式:
name = "Alice"
age = 30
height = 1.75
print(f"My name is {name}, I am {age} years old.") # 使用 f-string 插入变量值
# My name is Alice, I am 30 years old.
print(f"Next year, I will be {age + 1} years old.") # 可以在 f-string 中进行简单的表达式计算
# Next year, I will be 31 years old.
print(f"My height is {height:.2f} meters.") # 使用 f-string 格式化浮点数
# My height is 1.75 meters.自解释器
Python 中的内置函数 eval 可以将字符串解析成 Python 的代码
## test.py
num = 30
x = eval("num + 29")
print(x)$ python test.py
59创建与导入脚本
处理大型项目时,可以将代码分割,整理成多个 *.py 文件以便重复利用这些文件中的代码。这些 *.py 文件称为脚本(script)。如果你要导入的 Python 脚本与当前脚本位于同一个目录下,只需输入 import,然后是文件名,无需扩展名 .py
## demo.py
import useful_functions as uf
scores = [88, 92, 79, 93, 85]
mean = uf.mean(scores)
print("Demo Mean:", mean)
print("__name__ value:", __name__)
print("uf.__name__ value:", uf.__name__)## useful_functions.py
def mean(num_list):
return sum(num_list) / len(num_list)
def main():
n_list = [34, 44, 23, 46, 12, 24]
print("Useful Functions' Mean:", mean(n_list))
# 检测当前脚本(即 useful_functions.py)是否做为主模块被运行
# 当前脚本若是被其他脚本的 import 语句导入,则下面的 if main 块代码不运行
if __name__ == '__main__':
main()运行结果如下:
$ ls
demo.py useful_functions.py
$ python demo.py
Demo Mean: 87.4
__name__ value: __main__
uf.__name__ value: useful_functions人们通常只希望重复使用被导入脚本里的「函数」或「类」,而不是其他可执行代码(如 print()),所以为避免「脚本A」被导入后,其中的可执行语句被运行,应该要将这些语句包含在 if __name__ == "__main__"块中,或者,将它们包含在函数 main() 中并在上述 if main 块中调用该函数。
每当运行脚本时(*.py 文件),Python 会为所有模块(module)设置一个特殊的内置变量 __name__。这些模块包含导入模块(用 import 语句导入的),还有主模块(即当前脚本)。主模块的 __name__ 变量值会被设为字符串 "__main__",而对于被导入的模块,__name__ 变量会设为该模块的名称。因此 if main 代码块常用来判断是否为主模块。
PS:
从输出结果可看到,上述例子中,被导入模块的 __name__ 值为 "useful_functions"
自定义一个类 - 复杂例子
import math
from decimal import Decimal, getcontext
## 设置 Decimal 数据类型的小数点后保留的位数
getcontext().prec = 19
## 用来辅助判断一个数是否为零(小于此值则为零)
TOLERANCE = 1e-10
class Vector(object):
## 设置报错信息
ALL_ELEMENT_MUST_BE_NUM_MSG = "Element must be number!"
## 实例(instance)的创建
def __init__(self, coordinates):
try:
if not coordinates:
raise ValueError
self.coordinates = tuple([Decimal(x) for x in coordinates])
self.dimension = len(coordinates)
except ValueError:
raise ValueError('The coordinates must be nonempty')
except TypeError:
raise TypeError('The coordinates must be an iterable')
## print 输出内容
def __str__(self):
return 'Vector: {}'.format(self.coordinates)
## 定义相等
def __eq__(self, v):
return self.coordinates == v.coordinates
def __len__(self):
return len(self.planes)
## __getitem__ 这个函数使得对于 v = Vector([5, 3, -2]) ,可使用索引方式 v[1] 得到数字 3
def __getitem__(self, i):
return self.coordinates[i]
## __setitem__ 这个函数使得对于 v = Vector([5, 3, -2]) ,可使用索引方式 v[1] = 9 将 v[1] 的值设置为 9
def __setitem__(self, i, x):
try:
assert x.dimension == self.dimension # 如果设置的 x 不是数字,则报错
self.coordinates[i] = x
except AssertionError:
raise Exception(self.ALL_ELEMENT_MUST_BE_NUM_MSG)
## 计算向量的加法
def plus(self, v):
new_coordinates = [x+y for x,y in zip(self.coordinates, v.coordinates)]
return Vector(new_coordinates)
## 计算向量的减法
def minus(self, v):
return self.plus(v.times_scalar(-1))
## 计算向量的数乘
def times_scalar(self, c):
new_coordinates = [x*c for x in self.coordinates]
return Vector(new_coordinates)
## 计算向量与另一向量的点积(内积)
def dot_product_with(self, v):
new_coordinates = [x*y for x,y in zip(self.coordinates, v.coordinates)]
return sum(new_coordinates)
## 计算向量的模
def magnitude(self):
return Decimal(math.sqrt(self.dot_product_with(self)))
## 计算向量标准化后的向量(同方向上的单位向量)
def normalization(self):
try:
mag = self.magnitude()
return self.times_scalar(Decimal(1)/mag)
except ZeroDivisionError:
#print("You can't normalize Zero Vector!")
raise Exception("You can't normalize Zero Vector!")
## 计算向量与另一向量的夹角
def angle_with(self, v, in_degrees=False):
try:
mag_1 = self.magnitude()
mag_2 = v.magnitude()
if in_degrees:
return math.degrees(math.acos(self.dot_product_with(v)/(mag_1*mag_2)))
else:
return math.acos(self.dot_product_with(v)/(mag_1*mag_2))
except ZeroDivisionError:
#print("At least one of the vector is Zero Vector! No angle defined.")
raise Exception("One of the vector is Zero Vector! No angle defined.")
## 私有函数(private function)和变量,以两个下划线开头来命名
def __is_zero_vector(self):
return self.magnitude() < TOLERANCE
def is_parallel_to(self, v):
if v.__is_zero_vector() or self.__is_zero_vector():
return True
else:
s_m = self.normalization()
v_m = v.normalization()
return s_m.minus(v_m).__is_zero_vector() or s_m.plus(v_m).__is_zero_vector()
def is_orthogonal_to(self, v):
return abs(self.dot_product_with(v)) < TOLERANCE
## 向量的分解 - 计算向量水平方向的分量
def component_parallel_to(self, base_vactor):
u_b = base_vactor.normalization()
mag = self.dot_product_with(u_b)
return u_b.times_scalar(mag)
## 向量的分解 - 计算向量竖直方向的分量
def component_orthogonal_to(self, base_vactor):
c_p = self.component_parallel_to(base_vactor)
return self.minus(c_p)
## 计算向量与另一向量的叉乘
def cross_product_with(self, v):
if self.dimension != 3 or v.dimension != 3:
raise Exception("Both cross product vectors must be three dimensional")
if self.is_parallel_to(v):
return Vector([0 for i in range(self.dimension)])
else:
x = self.coordinates[1]*v.coordinates[2] - self.coordinates[2]*v.coordinates[1]
y = self.coordinates[2]*v.coordinates[0] - self.coordinates[0]*v.coordinates[2]
z = self.coordinates[0]*v.coordinates[1] - self.coordinates[1]*v.coordinates[0]
return Vector([x, y, z])
## 计算向量与另一向量的所围成的四边形的面积
def area_of_parallelogram_spanned_with(self, v):
new_self = self
new_v = v
if self.dimension == 2:
new_self = Vector(self.coordinates + (0,))
if v.dimension == 2:
new_v = Vector(v.coordinates + (0,))
return new_self.cross_product_with(new_v).magnitude()
## 计算向量与另一向量的所围成的三角形的面积
def area_of_triangle_spanned_with(self, v):
return self.area_of_parallelogram_spanned_with(v) / Decimal(2)常用模组 - Module
Python 的标准库里包含了常用的 Module ,使用 import 语句可导入
以下是一些实用的模组(Module):
sys:内建了很多好用的工具
import sys ## 工具一:sys.stdout.flush() ## 不断更新输出内容,而不是叠加输出(即每次删除上次的「显示结果」,然后显示当次的「显示结果」) for i_episode in range(1, num_episodes+1): # monitor progress if i_episode % 1000 == 0: print("\rEpisode {}/{}.".format(i_episode, num_episodes), end="") sys.stdout.flush()re :通过正则表达式在字符串中进行模式匹配
math :一些标准数学函数
fractions:此库最常用的是里面的
Fraction,分数(有理数)计算超方便from fractions import Fractionrandom :生成假随机数字,随机打乱序列并选择随机项
json :适用于读写 json 文件(面向网络开发)
datime :读取当前时间和日期
os :与操作系统交互的操作
import os os.chdir(path) # 更改当前的工作目录(working directory)csv :处理
.csv类型的文件zipfile :处理
.zip压缩文件collections :常见数据类型的实用扩展,包括 OrderedDict、defaultdict 和 namedtuple
timeit :显示简短的代码运行所花费的时间
cProfile 或 profile :显示大型项目运行所花费的时间
打赏