Python读书笔记之《Python编程:从入门到实践》

Python代码风格 pep8

第一部分 基础知识

Chapter 1 起步

  • 程序存在严重错误时,Python 将显示 traceback 日志,即错误报告。import traceback 用于提供关于异常或错误的堆栈跟踪的工具。

    import traceback
    
    Print ("hello python world!")
    
    ## ------------------------
    
    Traceback (most recent call last):
      File "/Users/lyz/python_work/hello_world.py", line 6, in <module>
        Print ("hello python world!")
    NameError: name 'Print' is not defined
    

Chapter 2 变量和简单的数据类型

  • 变量名命名规则:

    • 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打头。(从 Python 3.x 开始,Python 支持 Unicode 字符做变量名,也就是能用中文做变量名,但不建议。)
    • 变量名不能包含空格,但能使用下划线来分隔其中的单词。
    • 不要将 python 关键字和函数名用作变量名。
    • 变量名应既简短又具有描述性。
    • 慎用小写字母 l 和大写字母 O,因为它们可能被人错看成数字 1 和 0。
  • 标准 Python 约定的文件名/文件夹名:使用小写字母和下划线,如 simple_message.pysimple_messages.py

  • 字符串:

    • 字符串大小写切换:str.title() 首字母大写; str.upper()全大写; str.lower()全小写。

      message = "hello python!"
      print (message, message.title(), message.upper(), message.lower())
      ---
      hello python! Hello Python! HELLO PYTHON! hello python!
      
    • 字符串中使用变量值:可先在左引号前加上字母 ff 字符串,format 简写,【Python3】 ),再将插入的变量放在花括号内。Python 2/3 字符串拼接,使用 + 合并字符串。

      #! /usr/bin/env python3
      
      first_name = "l"
      last_name = "yz"
      full_name = f"{first_name} {last_name}"
      full_name2 = first_name + " " + last_name
      message = f"Hello, {full_name.title()}"
      
      print (full_name, full_name2)
      print (message)
      #---------------------
      l yz l yz
      Hello, L Yz
      
    • Python 能够找出字符串开头和末尾多余的空白。去除字符串右侧的空白 rstrip() 即 right strip; 去除字符串左侧的空白 lstrip(),即 left strip;去除字符串前后两端的空白 strip()。空白符(包括\n\r\t、‘ ``‘,即:换行、回车、制表符、空格)。注意:去除空白,但不修改原变量字符串值

      str_space = "python "
      print (str_space.rstrip()+"hello", str_space+"hello")
      # -------------
      pythonhello python hello
      
      # 永久删除空格 关联到变量,但内存地址已经发生改变,变为另一个变量
      str_space = "python   "
      print ("str_space address: " , id(str_space))
      str_space = str_space.rstrip()+"hello"
      print ("str_space rstrip address: " , id(str_space))
      # -------------
      str_space address:  4481957616
      str_space rstrip address:  4481970928
      
      # 使用 replace 删除字符串内所有的空格
      str_space = str_space.replace(" ", "")
      
    • 删除字符串前缀/后缀。 str_delete.removeprefix() suf.removesuffix(".py") 与删除空白一样, removeprefix() 也保持原始字符串不变。 【Python3】

    • 字符串语法错误

      # 单引号内不可包含撇号 报错 'hello 'python' -- 错误 --> 'hello \'python'
      # 双引号内可包含撇号  "hello 'python"  -- 正确
      
  • 在 Python 2 中,print 还不是函数只是语句,无需将要打印的内容放在括号内。从技术上说,Python 3 中的 print 是一个函数,因此括号必不可少。

    # python 2
    print "hello"
    print ("hello")
    
    # print 3 必须有括号
    print ("hello")
    
    • 使用两个乘号 ** 表示乘方 1.1**1.1=1.11053 3**2=9

    • 注意:浮点数加减乘除法结果包含的小数位数可能是不确定的。如果需要判断是否相等时要多加注意。

      1.4 * 0.2 = 0.27999999999999997
      0.2 + 0.1 = 0.30000000000000004
      
    • 注意:在 Python 2 中,整数除法的结果只包含整数部分,小数部分被删除。 请注意,计算整数结果时,采取的方式不是四舍五入,而是将小数部分直接删除。所以进行除法操作时最好先将至少一个变量变为浮点数。

      # python 2
      3/2 = 1
      3.0/2 = 1.5
      
      # python 3 将任意两个数相除,结果总是浮点数
      3/2 = 1.5
      3.0/2 = 1.5
      
      a = 3 + 2 # type(a) : int
      b = 3.0 + 2 # type(b) : float
      
    • 同时给多个变量赋值

      a,b,c = 1,2,3
      a=b=c=1
      
    • 常量,在程序的整个生命周期内都保持不变的变量。python 没有内置的常量类型,但程序员会使用全大写字母来指出应将某个变量视为常量。

  • Python 之禅

    import this
    
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    **Simple is better than complex.**
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!
    

Chapter 3 列表简介

  • 列表 list 由一系列按特定顺序排列的元素组成。
# 打印列表所有元素,指定位置元素
list = [1, 2, 3]
print (list, list[0], list[-1])
---
[1, 2, 3] 1 3

# 获取最后一个元素
list[-1]

list1= [2,2]
# 列表元素的增加三种方法 append extend insert
list.append(4) # 列表末尾添加元素4
list.extend(list1) # 将list1扩展入list
list.insert(0, 5) # 索引0的位置插入5

# 删除三种方法
del list[0]
list.pop() # 默认弹出list末尾元素,此时list 删除末尾元素,list.pop()返回删除的元素
list.pop(0) # 弹出索引0位置元素
list.remove(4) # 删除值为4的元素

# 修改
list[0] = 2 # 修改索引0位置的值为2

# 获取指定值的索引
list.index(4)
# 列表有多个该值时索引获取可使用enumerate
a = [1,2,3,2,2,4]
print ([i for i,j in enumerate(a) if j == 2])
---
[1, 3, 4]
  • 该使用 del 语句还是 pop() 方法的判断标准:如果你要从列表中删除一个元素,且不再以任何方式使用它,就使用 del 语句;如果你要在删除元素后还能继续使用它,就使用方法 pop()。

  • remove 默认删除第一个指定值的元素,如果列表中出现多次,需要循环判断是否删除了该值。

  • 列表删除三种方法:del 删除该索引值,且该值不可用;pop 删除该索引值,该值被取出可用,默认弹出末尾值;remove 通过值删除,且只能删除第一个指定的值。

  • 对列表进行排序。永久性排序:sort() 正序 sort(reverse=True) 反序 ;临时性排序:sorted(a) sorted(a, reverse=True)

    • 反向打印,永久性修改列表排序顺序,再次使用 reverse 可恢复
    a = [1,2,3,2,2,4]
    # 列表长度
    print (len(a))
    print (a.sort(reverse=True), a)
    print (sorted(a,reverse=True), a)
    print (a.reverse(), a)
    ---
    None, [4, 3, 2, 2, 2, 1]
    [4, 3, 2, 2, 2, 1], [4, 3, 2, 2, 2, 1]
    None, [1, 2, 2, 2, 3, 4]
    

Chapter 4 操作列表

  • 遍历打印列表内容

    magicians = ['alice', 'david', 'carolina']
    for magician in magicians:
        print (magician)
    
  • range

    • range 函数生成一系列数字。可以理解为 左闭右开、差一行为

      for value in range(1,5): # range 内指定两个参数
          print (value)  # 1~4
      # **注意:**range 只打印了 1-4。函数 range() 让 Python 从你指定的第一个值开始数,并在到达你指定的第二个值后停止,因此输出不包含第二个值(这里为5)。
      
      for value in range(6): # range 内指定一个参数,打印从零开始
          print (value) # 0~5
      
    • 使用 range() 创建数值列表
      可使用 list() 函数将 range() 的结果直接转换为列表。 numbers = list(range(1,6)) # [1,2,3,4,5]
      可指定步长生成数。 numbers = list(range(2,11,2)) # [2,4,6,8,10]

  • 列表推导式,将 for 循环和创建新元素的代码合并成一行,并自动追加新元素。 squares = [value**2 for value in range(1,11)]

  • 使用列表的一部分,支持类似 matlab 那种切片操作。 a[0:3] 同样也是 左闭右开,取到的列表索引为 0 1 2,不包括 3。 a[-3:] a[:3]

  • 列表复制的三种方式。切片、直接复制、deepcopy。

    **# 切片复制和deepcopy的两列表内存地址与原列表不同,所以列表无关联,修改其中一个列表的值,另一个不会受影响。 直接复制的两个列表内存地址相同,有关联。**
    import copy
    b = [1, 2, '3', 'test']
    c = b # 直接复制
    c_slice = b[:] # 切片
    c_dc = copy.deepcopy(b) # deepcopy
    print ('b: {}, c: {}, c_slice: {}, c_dc: {}'.format(id(b), id(c), id(c_slice), id(c_dc)))
    --- 
    b: 4458013504, c: 4458013504, c_slice: 4458012288, c_dc: 4458012160
    
    ---
    list1 = [0.1, 0.5]
    list3 =  [[2.36, 2.77], [-2.77, -2.36], [1.62, 2.05]]
    list4 = list3[:]
    print (list4) # [[2.36, 2.77], [-2.77, -2.36], [1.62, 2.05]]
    list3.append(list1)
    print (list4) # 随list3改变,[[2.36, 2.77], [-2.77, -2.36], [1.62, 2.05]]
    
    a = [1,2,3]
    b = a[:]
    a.append(3)
    print ("b: %s" % b) # b不随a改变, b: [1, 2, 3]
    

    在Python中,使用 [:] 进行列表切片时,会创建一个原列表的浅拷贝。对于包含简单数据类型(如整数、浮点数)的列表,浅拷贝会生成一个新的列表,但其元素是原列表中元素的引用。这意味着当原列表中的元素是可变对象(如列表),它们的引用被复制到新列表时,原列表和新列表中对于可变对象的引用都指向同一个对象。

    在第二个例子中,b = a[:] 同样是创建了 a 的浅拷贝,但由于 a 列表中的元素都是不可变对象(整数),因此 ba 指向的是不同的列表对象,互不影响。所以当你向 a 中添加了一个整数3时,b 并不会受到影响。

    列表是可变对象,因为它们支持在创建后对其进行添加、删除、替换等操作,例如使用 append()remove()pop() 等方法修改列表的内容。

    整数是不可变对象,一旦创建后,其值是不可更改的。例如,对整数执行加法、减法等操作会创建一个新的整数对象,而不会直接改变原来的整数对象。

    c = 1
    print (id(c)) # 367624021400
    c = 2
    print (id(c)) # 367624021376 已经重新创建对象
    

    当你对可变对象(如列表)进行拷贝时(浅拷贝或深拷贝),新创建的对象是一个新的副本,但其中的元素引用指向与原对象相同的对象。这意味着如果原列表中的元素是可变对象,新创建的列表仍然会引用同样的可变对象,所以对其中一个列表的修改可能会影响到另一个列表。而对于不可变对象(如整数),即使创建了拷贝,也不会受到原对象的影响。

    当对一个列表进行浅拷贝时,如果该列表中的元素是可变对象(如列表),那么新创建的列表仍然会引用相同的可变对象,因此对其中一个列表的更改可能会影响另一个列表。而当列表中的元素是不可变对象(如整数)时,不会受到原对象的影响。

  • Python 将不能修改的值称为不可变的,而不可变的列表被称为元组(tuple)。元组看起来犹如列表,但使用圆括号而不是方括号来标识。定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样。不能修改元组的元素,但可以给存储元组的变量赋值。元组不能 append、remove,但可以切片。

    cell = (200, 100)
    print (cell[0])
    # cell[0] = 100 # 报错 TypeError: 'tuple' object does not support item assignment
    cell = (100,100) # 虽然不能修改元组的元素,但可以给表示元组的变量赋值
    print (cell)
    
  • 相比于列表,元组是更简单的数据结构。如果需要存储的一组值在程序的整个生命周期内都不变,可使用元组。

  • 严格来说,元组是由逗号标识的,圆括号只是让元组看起来更整洁、更清晰。如果要定义只包含一个元素的元组,必须在这个元素后面加上逗号。 my_t = (3,)

Chapter 5 if 语句

Chapter 6 字典

  • 字典(Dictionary)
    字典也是 Python 语言中经常使用的一种数据类型。跟列表类似,字典是另外一种可存储任意类型的数据,并且字典储存的数据也是可以修改的。不同于列表的是,字典每个基本元素都包括两个部分:键(key)和键对应的值(value)。键和值之间用冒号 : 分割,每对元素之间用逗号 , 分割,整个字典的数据在大括号 {} 中。在字典中,键的内容是不可重复的。 键为不可变数据类型,值可以是任何数据类型。在这里键只支持字符串类型。字典最大的优势就是能在海量数据下利用“键”快速查找出想要的值, 当有很多数据需要存储的时候,我们给每个值都打个标签,也就是“键”;想要调用这个值时,字典能够利用这个标签快速帮我们找到它。但是如果标签重复了,字典不知道哪个值才是对的,就会报错。列表是根据排序来记录每项的值,但是字典是没有顺序的,所以同一字典,每次打印出的排序可能是不同的。“键”才是调用字典的关键元素。字典是基础的数据类型,所以变量也可以被赋值为字典。

    # 字典创建、修改、删除
    d = {}
    d['age'] = 18
    d['name'] = 'lyz'
    print (d)
    d['name'] = 'david'
    print (d)
    del d['name']
    
  • 和 list 比较,dict 有以下几个特点:
    查找和插入的速度极快,不会随着 key 的增加而变慢;
    需要占用大量的内存,内存浪费多。
    而 list 相反:
    查找和插入的时间随着元素的增加而增加;
    占用空间小,浪费内存很少。

  • 使用 get() 来访问值。

    使用放在方括号内的键从字典中获取感兴趣的值,可能会引发问题:如果指定的键不存在,就将导致 Python 显示 traceback,指出存在键值错误(KeyError)。为避免出现这样的错误,可使用 get() 方法在指定的键不存在时返回一个默认值。 get() 方法的第一个参数用于指定键,是必不可少的;第二个参数为当指定的键不存在时返回的值,如果未指定,则默认返回 None。 point_value = alien_0.get('point', 'No point value assigned.') 。所以如果指定的键有可能不存在,应考虑使用 get() 方法。

  • Python 支持对字典遍历。字典可用于以各种方式存储信息,因此有多种遍历字典的方式:可遍历字典的所有键—值对 d.items()、键 d.keys() 或值 d.values()

    d = {}
    d['age'] = 18
    d['name'] = 'lyz'
    print (d, len(d))
    
    for key, val in d.items():
        print (key, value)
    ---
    {'age': 18, 'name': 'lyz'} 2
    age 18
    name lyz
    
  • 在不需要使用字典中的值时,方法 keys() 很有用。 for key in d.keys(): print (key) for val in d.values(): print (val)

  • 按特定顺序遍历字典中的所有键,在 for 循环中对返回的键进行排序。 可使用 sorted() 函数来获得按特定顺序排列的键列表的副本。 for name in sorted(favorite_languages.keys()):

  • 如果列表中包含重复项,为剔除重复项,可使用集合(set)。 for language in set(favorite_languages.values()):

  • 可使用一对花括号直接创建集合,并用逗号分隔元素:

    languages = {'python', 'rust', 'C', 'python'}
    

    集合和字典很容易混淆,因为它们都使用一对花括号定义的。当花括号内没有键值对时,定义的很可能是集合。不同于列表和字典,集合不会以特定的顺序存储元素。

  • 有时候,需要将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套。你可以在列表中嵌套字典、在字典中嵌套列表甚至在字典中嵌套字典。

    # 列表中嵌套字典
    d0 = {'age': 18, 'height': 1.7}
    d1 = {'age': 17, 'height': 1.8}
    d2 = {'age': 15, 'height': 1.8}
    d3 = {'age': 22, 'height': 1.9}
    d = [d0, d1, d2, d3]
    print (d)
    ---
    [{'age': 18, 'height': 1.7}, {'age': 17, 'height': 1.8}, {'age': 15, 'height': 1.8}, {'age': 22, 'height': 1.9}]
    
    # 字典嵌套字典
    d_dict = {'lyz': d0, 'david': d1, 'Jim': d2, 'Hely': d3}
    print (d_dict['lyz']['age'])
    
    # 字典中嵌套列表: 一个键 对应 多个值
    

Chapter 7 用户输入和 while 循环

  • Python3 input 函数。程序暂停,等待用户输入。 Python2 raw_input 函数

    inp = input('tell me why:')
    print (inp)
    

Chapter 8 函数

  • 形参,即函数完成工作所需的信息;实参,即在调用函数时传递给函数的信息。

  • 向函数传递实参的方式:

    • 位置实参。要求实参的顺序与形参的顺序相同;

    • 关键字实参。其中每个实参都由变量名和值组成;

      describe_pet(animal_type='hamster', pet_name='harry')
      describe_pet(pet_name='harry', animal_type='hamster')
      
    • 默认值参数。编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python 将使用指定的实参值;否则,将使用形参的默认值。给形参指定默认值时,等号两边不要有空格

      def describe_pet(age, name='lily'):
          print ('age: {}, name: {}'.format(age, name))
      
      describe_pet(18, 'lily') # 位置实参
      describe_pet(name='lily', age=18) # 关键字实参
      describe_pet(18) # 默认值参数
      ---
      age: 18, name: lily
      age: 18, name: lily
      age: 18, name: lily
      

      注意:当使用默认值时,必须在形参列表中先列出没有默认值的形参,再列出有默认值的形参。 def describe_pet(name='lily', age): 错误。

  • 实参数量可选的实现。 可选形参特殊值做 Flag判断,省的重构。

    def get_formatted_name(first_name, last_name, middle_name=""): # middle_name=None
        if middle_name:
            ...
        else:
            ...
    
  • 禁止函数修改列表。 列表作为参数传递时,有时想保留原来列表作为存档,此时可向函数传递列表副本 list[:] 而不是原始列表 list

    虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由,否则还是应该将原始列表传递给函数。这是因为,让函数使用现成的列表可避免花时间和内存创建副本,从而提高效率。

    def test_python(pet_name):
        pet_name.pop()
    
    pet_name = ['dog', 'cat', 'pig']
    test_python(pet_name[:]) # 传递列表副本
    print (f"pet_name[:]: {pet_name}") # 原始列表内容不变
    test_python(pet_name)
    print (f"pet_name: {pet_name}") # 原始列表内容改变
    
    --- 
    pet_name[:]: ['dog', 'cat', 'pig']
    pet_name: ['dog', 'cat']
    
  • 传递任意数量的参数。形参名 la 中的星号让 Python 创建一个名为 la 的空元组,并将收到的所有值都封装到这个元组中。函数体内的 print 语句通过生成输出来证明 Python 能够处理使用一个值调用函数的情形,也能处理使用三个值来调用函数的情形。 *args通用形参名,用于收集任意数量的位置实参

    def describe_pet(*la):
        print (la)
    la = [1,2,3,4]
    describe_pet(la, 18, 'lily')
    ---
    ([1, 2, 3, 4], 18, 'lily')
    
    def my_function(*args):
        for arg in args:
            print(arg)
    
    my_function(1, 2, 3, 4, 5)
    
  • 有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。这种情况下,可将函数编写成能够接受任意数量的键值对。

    **user_info 中两个星号让 python 创建一个名为 user_info 的字典,包括函数收到的其他所有名值对。常用 **kwargs 用于收集任意数量的 关键字实参

    def build_profile(first, last, **user_info):
    
    user_profile = build_profile('albert', 'einstein',
    														 location='princeton',
    														 field='physics')
    
    ---
    def my_function(**kwargs):
        for key, value in kwargs.items():
            print(f"{key}: {value}")
    
    my_function(name="Alice", age=30, city="New York")
    
  • 导入模块的方法:

    • 【推荐】导入整个模块 import module_namemodule_name.function_name()

    • 【不推荐】导入特定函数 from module_name import function_namefunction_name()

    • 【不推荐】使用 as 给函数、模块指定别名 from module_name import function as alias_namealias_name()

      【推荐】import module_name as alias_namealias_name.function()

    • 【不推荐】导入模块中所有函数 from module_name import *function()

  • 函数编写指南:

    • 函数命名应指定描述性名称,用小写字母与下划线命名;
    • 用文档字符串格式注释,简述其功能;
    • 给形参指定默认值时,等号两边不加空格,在函数调用中的关键字实参同理;
    • 若程序或模块包含多个函数,可使用两个空行将相邻的函数分开;
    • 所有的 import 语句都应放在文件开头;
    • 注释清晰明了,最好不要让别人需要看你的代码才知道你在写什么。

Chapter 9 类

  • 类创建对象成为实例化。类中的函数称为方法。

  • 我们通常可以认为首字母大写的名称(如 Dog)指的是类,而小写的名称(如 my_dog)指的是根据类创建的实例。

    • 因为我们创建的 Dog 类是全新的类,所以定义时不加括号。Python 允许定义时省略类名后的括号,因为类定义中的括号通常用于指定基类(继承),如果没有基类,那么括号可以省略。在 Python 2.x 中,通常需要使用带括号的形式来定义新式类,但在 Python 3.x 中已经没有这个区别。
    • __init__() 是一个特殊方法,当实例化 Dog 类时,Python 会自动运行它,开头和末尾各有两个下划线。方法的形参 self 必不可少,且必须位于其他形参前。方法定义中包含 self 的原因是 当 Python 调用这个方法来创建 Dog 类时,将自动传入实参 self。每个与实例相关联的方法调用都会自动传递实参 self,该实参是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
    class Dog:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def sit(self):
            print (f"{self.name} is now sitting.")
    
        def roll_over(self):
            print (f"{self.name} rolled over!")
    
    dog = Dog('lily', 18)
    dog.sit()
    dog.roll_over()
    
  • 类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。

  • 三种修改属性的值的方法:直接通过实例修改、通过方法设置,以及通过方法递增。

    # 直接实例修改
    my_new_car = Car(...)
    my_new_car.odometer_reading = 23
    
    # 方法修改
    class Car():
        ...
    
        def update_odometer(self, mile):
            self.odometer_reading = mile
    
    my_new_car = Car(...)
    my_new_car.update_odometer(23)
    
    # 递增修改
    class Car():
        ...
        def increment_odometer(self, mile):
            self.odometer_reading += mile
    
  • 继承

    • 如果要编写的类是一个既有的类的特殊版本,可使用继承。当一个类继承另一个类时,将自动获得后者的所有属性和方法。原有的类成为父类(parent class),而新类称为子类(child class)。子类不仅继承了父类的所有属性和方法,还可定义自己的属性和方法。
    • 在既有的类的基础上编写新类,通常要调用父类的 __init__() 方法。
    class Car():
    
        def __init__(self, name, model, year):
            self.name = name
            self.model = model
            self.year = year
    
        def print_info(self):
            print ("name: {}, model: {}, year: {}".format(self.name, self.model, self.year))
            # name: audi, model: a6, year: 2023
    
    class ElecCar(Car):
        def __init__(self, name, model, year):
            # 初始化父类的属性
            super().__init__(name, model, year)
    				self.battery_size = 40
    
        def print_battery_info(self):
            print ("battery size: {}".format(self.battery_size))
    
        def print_info(self): # 重写父类方法
            print ("name: {}, model: {}, year: {}".format(self.name, self.model, self.year+1)) 
            # name: audi, model: a6, year: 2024
    
    ev = ElecCar('audi', 'a6', 2023)
    ev.print_info() # 调用父类方法
    ev.print_battery_info() # 调用子类方法 
    
    • super() 是一个特殊函数,能够调用父类的方法。这行代码让 Python 调用 Car 类的 __init__() 方法。

    • 重写父类中的方法:在子类中定义一个与要重写的父类方法同名的方法,Python 将忽略这个父类方法,只关注子类中定义的相应方法。

    • 以上代码随着属性和方法的增多,可将部分属性和方法提取出来,将大型类拆分成多个协同工作的小类,这种方法称为组合。如下提取电池信息:

      class Car():
      
          def __init__(self, name, model, year):
              self.name = name
              self.model = model
              self.year = year
      
          def print_info(self):
              print ("name: {}, model: {}, year: {}".format(self.name, self.model, self.year))
      
      class ElecCar(Car):
          def __init__(self, name, model, year):
              super().__init__(name, model, year)
              self.battery = Battery() # 实例化Battery类
      
          def print_info(self):
              print ("name: {}, model: {}, year: {}".format(self.name, self.model, self.year+1))
      
      class Battery():
          def __init__(self, battery_size=40):
              self.battery_size = battery_size
      
          def print_battery_info(self):
              print ("battery size: {}".format(self.battery_size))
      
      ev = ElecCar('audi', 'a6', 2023)
      ev.print_info()
      ev.battery.print_battery_info()
      
  • 合适的工作流程:一开始应让代码结构尽量简单。首先尝试在一个文件中完成所有的工作,确定一切都能正确运行后,再将类移到独立的模块中。

Chapter 10 文件和异常

  • pathlib 读写文件内容。默认从 Python 3.4 版本开始引入。

    from pathlib import Path
    path = Path('/Users/lyz/1.log')
    if path.exists():
        print (path.read_text()) # 末尾回多一个空行
        print (path.read_text().rstrip()) # 删除末尾空行
    content = "hello python"
    print (path.write_text(content)) # 写入
    
  • 在 path 对象调用 write_text() 方法时,务必谨慎。如果指定的文件已经存在,write_text() 将删除其内容,并将指定的内容写入其中。

  • splitlines() 方法返回一个列表,将冗长的字符串转换成一系列行进行处理。

  • 读取整个文件作为字符串存入,read()
    每次读取一行作为字符串存入,readline()
    读取每一行写入一个列表,readlines()
    只读打开文件,with open(filename,'r')
    写入文件,with open(filename,'w'),这样打开默认写入的时候覆盖之前的内容,with opent(filename,'a'),写入文件的时候在末尾附加
    读写打开文件,with open(filename,'r+')

  • 关键字 with 在不再需要访问文件后将其关闭。你也可以调用 open() 和 close() 来打开和关闭文件,但这样做时,如果程序存在 bug,导致 close() 语句未执行,文件将不会关闭。

    # 2
    1111
    2222
    3333
    4444
    ----
    with open('2', 'r') as f:
        for line in f.readlines():
            print (line.rstrip())
            print ('-------')
    
    fr = open('2', 'r')
    for line in fr.readlines():
        print (line.rstrip())
        print ('-------')
    fr.close()
    
    with open('2', 'r') as f:
        contents = f.read()
        print (contents)
    
    # 调用open()后,将一个表示文件及其内容的对象存储到了变量file_object中
    with open('2', 'r') as f:
        for line in f:
            print (line.rstrip())
            print ('-------')
    
    # 文件内容转存列表
    with open(r'/Users/lyz/lyz_ws/2', 'r') as f:
        lines = f.readlines() # ['1111\n', '2222\n', '3333\n', '4444']
    print (lines)
    for line in lines:
        if len(line.strip()) > 0:
            print (line.rstrip())
            # line_float = float(line) # 会强制将换行符删除
            # print (line_float)
    
  • 由于反斜杠在 Python 中被视为转义标记,为在 Windows 中确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号前加上 r。

  • 读取文本文件时,Python 将其中的所有文本都解读为字符串。如果你读取的是数字,并要将其作为数值使用,就必须使用函数 int() 将其转换为整数,或使用函数 float() 将其转换为浮点数。

  • Python 只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数 str() 将其转换为字符串格式。

  • 可使用方法replace()将字符串中的特定单词都替换为另一个单词。

    msg = 'I really like you!'
    mm = msg.replace('you', 'me')
    print (mm)
    
  • 异常

    • 异常是使用 try-except 代码块处理的。try-except 代码块让 Python 执行指定的操作,同时告诉 Python 发生异常时怎么办。使用了 try-except 代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的 traceback。

      try:
          with open(r'/Users/lyz/lyz_ws/3', 'r') as f:
              contents = f.read()
      except FileNotFoundError:
          msg = 'Sorry, the file '+'3'+' not found!'
          print (msg)
      --- 
      Sorry, the file 3 not found!
      
      with open(r'/Users/lyz/lyz_ws/3', 'r') as f:
          contents = f.read()
      ---
      File "/Users/lyz/lyz_ws/hello.py", line 78, in <module>
          with open(r'/Users/lyz/lyz_ws/3', 'r') as f:
      FileNotFoundError: [Errno 2] No such file or directory: '/Users/lyz/lyz_ws/3'
      
    • 通过将可能引发错误的代码放在 try-except 代码块中,可提高程序抵御错误的能力。另外 try-except 还可加 else 代码块,只有 try 代码块成功执行才需要继续执行的代码,都应放在 else 代码块中。

    • 使用 try-except 代码块的两个重要的优点:一是避免用户看到 traceback,二是让程序可以继续分析能够找到的其他文件。

    • 静默失败。失败时一声不吭 pass

  • JSON(JaveScript Object Notation)

    • 使用 json.dump() 和 json.load() 存储数据。json.dumps() 函数接受一个实参,即要转换为 JSON 格式的数据。 json.loads() 将 JSON 格式的字符串作为参数,并返回一个 Python 对象。
    import json
    
    b = [1, 2, '3', 'test']
    fn = 'test.json'
    with open(fn, 'w') as f:
        contents = json.dumps(b)
        f.write(contents)
    f.close()
    ---
    [1, 2, '3', 'test']
    
    with open(fn, 'r') as f:
        nums = json.load(f)
    print (nums)
    ---
    [1, 2, '3', 'test']
    

Chapter 11 测试代码

  • pytest 库是一组工具,不仅可以快速而轻松的编写测试,而且能持续支持随项目增大而变得复杂的测试。 python 默认不包含 pytest。 python -m pip install --user pytest --user 标志让 Python 只为当前用户安装指定的包。如果在执行这个命令时遇到麻烦,可尝试在不指定标志 --user 的情况下再次执行。

    • 测试文件的名称必须以 test_ 开头。
    • 终端切换到测试文件所在的文件夹,执行命令 pytest。如果仅需要测试某个 py 文件 pytest test_1.py
    $ pytest
    ====================================================================== test session starts =======================================================================
    platform darwin -- Python 3.9.1, pytest-7.4.2, pluggy-1.3.0
    rootdir: /Users/lyz/python_work
    collected 2 items                                                                                                                                                
    
    test_1.py ..                                                                                                                                               [100%]
    
    ======================================================================= 2 passed in 0.01s ========================================================================
    
    • pytest 各种断言语句:

      assert a == b  # 断言两个值相等
      assert a != b  # 断言两个值不等
      assert a       # 断言 a 的布尔求值为 True
      assert not a   # 断言 a 的布尔求值为 False
      assert element in list # 断言元素在列表中
      assert element not in list # 断言元素不在列表中
      
    • 在测试中,夹具(fixture)可帮助我们搭建测试环境。这通常意味着创建供多个测试使用的资源,在 pytest 中,要创建夹具,可编写一个使用装饰器 @pytest.fixture 装饰的函数。

      # anonymous_survey.py
      class AnonymousSurvey():
          def __init__(self, question):
              self.question = question
              self.responses = []
      
          def show_question(self):
              print(self.question)
      
          def store_response(self, new_response):
              self.responses.append(new_response)
      
          def show_results(self):
              print("Survey results:")
              for response in self.responses:
                  print(f"- {response}")
      
      # test_anonymous_survey.py
      import pytest
      from anonymous_survey import AnonymousSurvey
      
      @pytest.fixture  # 夹具
      def language_survey():
          question = "What language did you first learn to speak?"
          language = AnonymousSurvey(question)
          return language
      
      def test_store_single_response(language_survey):
          language_survey.store_response("English")
          assert 'English' in language_survey.responses
      
      def test_store_threse_response(language_survey):
          responses = ['English', 'Spanish', 'Mandarin']
          for response in responses:
              language_survey.store_response(response)
      
          for response in responses:
              assert response in language_survey.responses
      
    • 将装饰器 @pytest.fixture 应用于新函数 language_survey() ,函数返回 AnonymousSurvey 对象。当测试函数的一个形参与应用了装饰器 @pytest.fixture 的函数(夹具)同名时,将自动运行夹具,并将夹具返回的值传递给测试函数。

    • 如果编写的测试包含大量重复的代码,夹具的方式可用来消除重复的代码。另外对于简单的测试,使用夹具并不一定能让代码更简洁、更容易理解;但在项目包含大量测试或需要使用很多行代码来创建供多个测试使用的资源的情况下,使用夹具可极大的改善测试代码的质量。

  • unittest

    • Python 标准库中的模块unittest提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
    '''
    car.py
    brand: bmw, benz, audi, vw, toyota, land rover
    type: orv or sedan
    model: 740Li, G63, RS7, Phaeton, Land Cruiser, Discovery
    '''
    class Car:
        def __init__(self, brand, type, model):
            self.car_brand = brand
            self.car_type  = type
            self.car_model = model
    
        def describe_car(self):
            print ("car info: {} {} {}".format(self.car_brand.title(), self.car_type.title(), self.car_model.title()))
            return self.car_brand
    
    class ElectricCar(Car):
        def __init__(self, brand, type, model):
            super().__init__(brand, type, model)
            
    # car = Car('benz', 'orv', 'g63')
    # car.describe_car() 
    
    def describe_car(brand, type, model):
        print ("car info: {} {} {}".format(brand.title(), type.title(), model.title()))
        return brand
    
    ---
    '''
    test.py
    '''
    import unittest
    
    from car import Car
    from car import describe_car
    
    class CarTestCase(unittest.TestCase):
        def test_brand(self):
            brand = describe_car('benz', 'orv', 'g63')
            self.assertEqual(brand, 'benz')
    
        def test_class_brand(self): # 测试类
            cc = Car('benz', 'orv', 'g63')
            brand = cc.describe_car()
            self.assertEqual(brand, 'benz')
    
    unittest.main()
    ---
    '''
    car info: Benz Orv G63
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    
    OK
    '''
    
    • 你可随便给这个类命名,但最好让它看起来与要测试的函数相关,并包含字样 Test。这个类必须继承unittest.TestCase类,这样 Python 才知道如何运行你编写的测试。所有以 test 打头的方法都将自动运行
    • 代码行unittest.main()让 Python 运行这个文件中的测试。
    • Python 在 unittest.TestCase 类中的断言方法
      1⃣️ assertEqual(a,b) —— 断言 a==b
      2⃣️ assertNotEqual(a,b) —— 断言 a != b
      3⃣️ assertTrue(x) —— 断言 x 为 true
      4⃣️ assertFalse(x) —— 断言 x 为 False
      5⃣️ assertIn(item, list) —— 断言 item 在列表 list 中
      6⃣️ assertNotIn(item, list) —— 断言 item 不在列表 list 中
    • unittest.TestCase 类包含方法 setUp(),让我们只需创建这些对象一次,并在每个测试方法中使用它们。如果你在 TestCase 类中包含了方法 setUp(),Python 将先运行它,再运行各个以 test_ 打头的方法。这样,在你编写的每个测试方法中都可使用在方法 setUp() 中创建的对象了。注意,每次在运行以 test_ 打头的方法时,setUp 都会运行一次,而不是只在测试开始时运行一次。
    • 可在 setUp() 方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。
    • 运行测试用例时,每完成一个单元测试,Python 都打印一个字符:测试通过时打印一个句点;测试引发错误时打印一个 E;测试导致断言失败时打印一个 F。

第二部分 项目

项目 1 外星人入侵

项目 2 数据可视化

  • matplotlib 是专门用于开发 2D 图表(包括 3D 图表),使用起来极其简单,以渐进、交互式方式实现数据可视化。能将数据进行可视化,更直观的呈现,使数据更加客观、更具说服力。

    import matplotlib.pyplot as plt
    
    squares = [1, 4, 9, 16, 25]
    # method 1
    plt.plot(squares, color='r', lw=1)
    plt.xlabel('Value')
    plt.ylabel('Square of Value')
    plt.grid()
    plt.legend()
    plt.title('Figure 1')
    # method 2
    # fig, ax = plt.subplots()
    # ax.plot(squares, linewidth=3)
    # ax.set_xlabel("Value", fontsize=14)
    # ax.set_ylabel("Square of Value", fontsize=14)
    # ax.tick_params(labelsize=14) # 设置刻度标记的样式
    plt.show()
    
    ---
    import matplotlib.pyplot as plt
    
    squares = [1, 4, 9, 16, 25]
    input_values = [1, 2, 3, 4, 5]
    # method 1
    plt.plot(input_values, squares, color='r', lw=1)
    # 设置x轴刻度45度旋转显示,并且尾部对齐刻度。(ha 参数(horizontal alignment)来设置标签的水平对齐方式为 'right'(右对齐))
    plt.xticks(rotation=45, ha='right')
    plt.xlabel('Value')
    plt.ylabel('Square of Value')
    plt.grid()
    plt.legend()
    plt.title('Figure 1')
    # method 2
    # fig, ax = plt.subplots()
    # ax.plot(input_values, squares, linewidth=3)
    # ax.set_xlabel("Value", fontsize=14)
    # ax.set_ylabel("Square of Value", fontsize=14)
    # ax.tick_params(labelsize=14) # 设置刻度标记的样式
    plt.show()
    
    ---
    # 如果希望show的图像通过键盘控制关闭,而非手动X关闭
    plt.show(block=False)
    #raw_input("按Enter键关闭图像") # python2
    input("按Enter键关闭图像...") # python3
    plt.close()
    

    linewidth :线条的粗细; set_title() :设置标题;fontsize :文字大小; set_xlabel()set_ylabel() 方法设置每个轴的标题; tick_params() 方法设置刻度标记的样式。

    • scatter() 绘制散点图

      import matplotlib.pyplot as plt
      
      fig, ax = plt.subplots()
      ax.scatter(2,4,s=200, color='red') # 绘制 (2, 4) 单点,s参数设置点的尺寸
      plt.show()
      
      ------
      # 绘制散点图
      import matplotlib.pyplot as plt
      
      squares = [1, 4, 9, 16, 25]
      input_values = [1, 2, 3, 4, 5]
      fig, ax = plt.subplots()
      ax.scatter(input_values, squares, s=20)
      ax.axis([0, 30, 0, 30]) # axis方法指定每个轴的取值范围
      ax.set_aspect('equal') # 指定两条轴上刻度的间距必须相等
      ax.get_xaxis().set_visible(False) # 隐藏x坐标轴
      ax.get_yaxis().set_visible(False) # 隐藏y坐标轴
      plt.savefig('test.png', dpi=1600) # 保存图片
      plt.show()
      
    • Plotly 生成交互式图形。当用户将鼠标指向特定的元素时,将显示有关该元素的信息。可以使用 Plotly Express 来创建初始图形,Plotly Express 是 Plotly 的一个子集。

      import plotly.express as px
      
      squares = [1, 4, 9, 16, 25]
      input_values = [1, 2, 3, 4, 5]
      fig = px.bar(x=input_values, y=squares)
      fig.write_html('test.html')
      
      fig.show()
      
  • CSV,comma-separated values 以逗号分隔的值。 csv 模块包含在 Python 标准库中,可用于解析 CSV文件中的数据行。当以 reader 对象为参数时,函数 next() 返回文件中的下一行(从文件头开始),通常用于打印文件头。

    # 打印文件头
    import csv
    
    file = "/Users/lyz/Downloads/DVT2/test/1.csv"
    
    with open(file, 'r') as f:
        reader = csv.reader(f)
        header_row = next(reader)
        print (header_row)
    
        for index, colume in enumerate(header_row):
            print (index, colume)
    
  • Git 版本控制

    git --version # 版本查询
    
    # git 配置
    git config --global user.name "username"
    git config --global user.email "username@example.com"
    
    git init # 初始化仓库
    git status # 检查状态
    git add . # 将文件加入仓库
    git commit -m "Started project." # 创建项目的快照,标志 -m 让 git将接下来的信息记录到项目的历史记录中。 执行提交
    git log # 查看历史提交记录。 每次git提交时,都会生成一个独一无二的引用ID,长度为40个字符。它记录提交是谁执行的,提交的时间以及提交时指定的信息。
    git log --pretty=oneline # 标志 --pretty=oneline 指定显示两项最重要的信息:提交的引用ID 以及提交记录的信息。
    git restore .  # 放弃修改。 git restore filename 放弃指定文件所做的修改
    git reset --hard 引用ID前六个字符 # 指定要永久恢复到的提交的引用ID的版本
    rm -rf .git/ # 删除仓库
    
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值