快速熟悉 Python

    Python

由于很多内容都可以 Google ,这里只简单介绍一些 Python 重要的基本操作,方便未来完全忘记之后可以根据这篇文章很快上手。


Python 编辑器

一般推荐两个编辑器,或者说 IDE ,来快速实践各种代码:

此外,还可以选择 安装配置 Anaconda ,以使用 Anaconda 附带的 Python 在终端里运行。

# 查看 Python 的版本
$ python --version
Python 3.6.5 :: Anaconda custom (64-bit)

# 运行 *.py 的 Python 脚本文件
$ cat ~/scripts/first_script.py
print('Congratulations on running this script!!')

$ python ~/scripts/first_script.py
Congratulations on running this script!!

# 直接输入命令 python ,不带任何文件和参数,会进入 interactive interpreter(交互式解释器),可进行各种 Python 的语法测试
$ python
[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 的强大功能,关键是安装配置起来特别简单,最适合新手。


Python 的一些基本规则

  1. 变量区分大小写,即 case sensitive
  2. 空格重要,附带语意,即 spacing matters
  3. 运算符号有优先级(比如乘除法 * / 优先级高于加减法 +-)
  4. 运算的优先级可以使用括号来确定:优先级方向为从里往外
  5. Python 中名为 _ 的特殊变量(underscore variable)参见这里
  6. Python 赋值时是「传址」效果,而不是「传值」效果。见下例:

    # 感受一下「传址」效果
    def li_round_change_value(li):
     for i,l in enumerate(li):
       li[i] = round(float(l), 3)
    
    def li_round_doesnt_change_value(li):
     # 使用 map(function_to_apply, list_of_inputs) 将 list 里元素的类型变为浮点(float)型
     li = list(map(lambda k:round(float(k), 3), li))
     return li
    
    test_li = [9,13,5,2]
    print(li_round_doesnt_change_value(test_li)) # 输出 [9.0, 13.0, 5.0, 2.0] 
    print(test_li) # 输出 [9, 13, 5, 2]
    
    li_round_change_value(test_li)
    print(test_li) # 输出 [9.0, 13.0, 5.0, 2.0]
    

附:
Python 命名规范


Python 中的注释

  • # 开头的行
  • """(三个双引号)包裹的代码块(Code Block)
# codes by chpwang - 2018-05-17 04:57:56

"""
任务3:
xxxxxx
"""

输出

这里介绍的是常用的输出语法,更详细的介绍可以参考这篇 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 中的内置函数 eval 可以将字符串解析成 Python 的代码

# test.py
num = 30
x = eval("num + 29")
print(x)
$ python test.py
59

字符串 - String

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', '']

列表 - List

>>> li = ['1', '2', '3']

# 计算列表长度
>>> len(li)
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]

字典 - Dictionary

字典本质上就是键值对(key-value pair),比如对于 {'Ace': 1, 'Queen': 12} 这一字典来说,‘Ace’‘Queen’ 就是(Key),而对应的 112 就是(Value)

# 判断某个 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

分支 - if 语句

if season == 'spring':
    print('plant the garden!')
elif season == 'summer':
    print('water the garden!')
else:
    print('unrecognized season')


# lambda 中的 if - 无法在 lambda 匿名函数中使用 print 或者 raise
lambda x: True if x % 2 == 0 else False

循环

# 和 range(start, stop, step) 函数结合使用
>>> for i in range(0, 2, 1):
...     print(i)
...
0
1

# 和 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

创建与导入脚本

处理大型项目时,可以将代码分割,整理成多个 *.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 Fraction
    
  • random :生成假随机数字,随机打乱序列并选择随机项

  • json :适用于读写 json 文件(面向网络开发)

  • datime :读取当前时间和日期

  • os :与操作系统交互的操作

    import os
    os.chdir(path)  # 更改当前的工作目录(working directory)
    
  • csv :处理 .csv 类型的文件

  • zipfile :处理 .zip 压缩文件

  • collections :常见数据类型的实用扩展,包括 OrderedDict、defaultdict 和 namedtuple

  • timeit :显示简短的代码运行所花费的时间

  • cProfile 或 profile :显示大型项目运行所花费的时间


打赏