selfteaching-learning-notes.github.io icon indicating copy to clipboard operation
selfteaching-learning-notes.github.io copied to clipboard

1901050119-自学训练营 - 《自学是门手艺》- 学习笔记

Open Galaxy1227 opened this issue 6 years ago • 3 comments

学员信息

  • 学号:1901050119
  • 学习内容:《自学是门手艺》

学习笔记

  • 姓名:Galaxy
  • 学习时间:20190905
  • 学习内容:3.1~3.2.1 类——面向对象编程
  • 学习用时:1.5h

收获总结:

这部分的重点是概念的理解与区分,之前读的时候,感觉很努力地去理解,好像也确实理解了,但却不那么牢靠。转眼放到一堆,又懵了。这次明显感觉清晰多了,理解更透彻了,就像双胞胎看久了,终于能够很轻易地分辨了。

Daliy Tips:

  1. 所有的难点,事实上都可以被拆解成更小的单元,而后在逐一突破的时候,就没那么难了。逐一突破全部完成之后,再拼起来重新审视的时候就会发现那所谓的难常常只不过是错觉、幻觉而已 —— 我把它称为困难幻觉

  2. 基本术语

这种被保留下来的 “必要的特征”,叫做对象的属性(Attributes),抽象过后被保留下来的 “必要的行为”,叫做对象的方法(Methods)。

从用编程语言创造对象的角度去看,所谓的界面,就由这两样东西构成:

  • 属性 —— 用自然语言描述,通常是名词(Nouns)
  • 方法 —— 用自然语言描述,通常是动词(Verbs)

“面向对象编程” 方法论最基本的术语

  • 对象,封装,抽象

  • 界面,属性,方法

  • 继承,类,子类,实例

  • 姓名:Galaxy

  • 学习时间:20190827

  • 学习内容:2.4.8 ~ 2.5 可执行的 Python 文件

  • 学习用时:3h

收获总结:

关于命令行相关的操作,基本零基础的我,只能机械照抄书上的命令,结果当然很挫败。后来在群里求助,得到学霸群友的指点和帮助,总算有了一些朦胧的认知,至少知道问题出在哪了。然后艰难补课中……

Daliy Tips:

理论上来讲,最终可以把任何一个程序,无论大小,都封装(或者囊括)到仅仅一个函数之中。按照惯例(Convention),这个函数的名称叫做 main()

  • 当一个模块被 import 语句导入的时候,这个模块的 name 就是模块名(例如:'mycode')。
  • 而当一个模块被命令行运行的时候,这个模块的 name 就被 Python 解释器设定为 'main'。

把一个程序整个封装到 main() 之中,而后在模块代码里加上:

if name == 'main': main()

这么做的结果是:

  • 当 Python 文件被当作模块,被 import 语句导入时,if 判断失败,main() 函数不被执行;
  • 当 Python 文件被 python -m 运行的时候,if 判断成功,main() 函数才被执行。

补课

  1. PATH 的设置

PATH 指的是“环境变量”,设置环境变量的目的,是让某个位置的程序在任何位置都能运行。

举例来说,装好 Anaconda3 后,随带的 Python 所在的路径是 C:\Users<UserName>\Anaconda3\python.exe,在没有设置环境变量的情况下,Python 只能在这个文件夹下运行,在其他位置运行——比如 GitHub Desktop 管理的 hello-world 文件夹 C:\Users<UserName>\Documents\GitHub\hello-world\ ——的时候,需要输入完整的路径才可以。也就是说,运行一下“Hello, World!”程序,就需要在其所在的文件夹输入这样的指令:

C:\Users<UserName>\Anaconda3\python.exe hello-world.py

这显然相当麻烦。 如果把 Python 所在的位置加入了环境变量,同样的工作,只需要这样输入就行了:

python hello-world.py

影响其实还不仅如此,不设置环境变量的话,很多程序自动化的功能,都会因为找不到需要的文件失效,所以设置环境变量是必不可少的一步。

设置的方法是: i. 依次打开【控制面板】-【系统和安全】-【系统】-【高级系统设置】-【环境变量(N)...】 ii. 在【系统变量(S)】区域双击【Path】 iii. 使用右侧的【新建(N)】按钮添加如下几行

  • C:\Users<UserName>\Anaconda3
  • C:\Users<UserName>\Anaconda3\Scripts
  • C:\Users<UserName>\Anaconda3\Lib
  • C:\Users<UserName>\Anaconda3\Library\bin
  • C:\Users<UserName>\Anaconda3\Library\mingw-w64\bin

注意此处的 <UserName> 需要更换为自己的账户。如果修改了默认安装的文件夹,那就按实际文件夹位置填写,要对应包含这几个位置,不要有错漏。

参考链接

其实现在常用的似乎就是

  • 切换到某个盘(输入“盘符:”回车)、
  • 进入某个文件夹(输入“CD 文件夹名”回车)
  1. "#!/usr/bin/env python" 是什么意思

On unix systems, Python scripts can be made executable using the following process:

  • Add this line as the first line in the script: #!/usr/bin/env python3.
  • At the unix command prompt, type the following to make myscript.py executable: ...
  • Move myscript.py into your bin directory, and it will be runnable from anywhere.

这个在unix类的操作系统才有意义。 "#!/usr/bin/python"是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python解释器; "#!/usr/bin/env python"这种用法是为了防止操作系统用户没有将python装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找python的安装路径,再调用对应路径下的解释器程序完成操作。 在windows中设置了环境变量后可以直接“hello.py”

或者这样解释: 加上"#!/usr/bin/env python", 这个py就处于了可执行模式下, (当然是针对linux类的操作系统), 这个hint, 告诉操作系统要使用哪个python解释器来执行这个py。在linux上执行一下命令 /usr/bin/env python ,就知道这行其实是call一下python解释器。这种写法比#! /usr/bin/python要好, 后者是hard coding 了python的路径。

参考链接:

Python 的操作符优先级 Operator precedence Python 的更多彩蛋 Python Easter Eggs

  1. 刻意思考
  • 刻意思考哪儿需要刻意练习
  • 这东西能用在哪儿呢?
  • 这东西还能用在哪儿呢?

MoSCoW Method

  • Must have

  • Should have

  • Could have

  • Won't have

  • 姓名:Galaxy

  • 学习时间:20190825

  • 学习内容:2.4.7 测试驱动的开发

  • 学习用时:2.5h

收获总结:

今天内容所涉及的测试驱动开发、单元测试、错误处理等,都只是先建立初步概念,更多细节要等后面返回来仔细研究。拓展阅读——单元测试的内容太多太琐碎,实在看不下去了,需要用时再看吧。

Daliy Tips:

  1. 测试驱动的开发

“通过先想办法验证结果而后从结果倒推” 的开发方式,叫做 “Test Driven Development”,以测试为驱动的开发。

如果我写的 is_leap(year) 是正确的,那么:

  • is_leap(4) 的返回值应该是 True
  • is_leap(200) 的返回值应该是 False
  • is_leap(220) 的返回值应该是 True
  • is_leap(400) 的返回值应该是 True

以上四种情况,其实只不过是根据算法 “考虑全面” 之后的结果

def is_leap(year): pass

is_leap(4) is True is_leap(200) is False is_leap(220) is True is_leap(400) is True

考虑到更多的年份不是闰年,所以,排除顺序大抵上应该是这样:

  • 先假定都不是闰年;
  • 再看看是否能被 4 整除;
  • 再剔除那些能被 100 整除但不能被 400 整除的年份……

def is_leap(year): r = False if year % 4 == 0: r = True if year % 100 == 0: if year % 400 !=0: r = False return r

is_leap(4) is True is_leap(200) is False is_leap(220) is True is_leap(400) is True

  1. Exception

在 Python 中,定义了大量的常见 “意外”,并且按层级分类:

在第三部分阅读完毕之后,可以回来重新查看以下官方文档: https://docs.python.org/3/library/exceptions.html

在 Python 中,可以用 try 语句块去执行那些可能出现 “意外” 的语句,try 也可以配合 except、else、finally 使用。

try: f = open('test_file.txt', 'r') except FileNotFoundError as fnf_error: print(fnf_error)

更多关于错误处理的内容,请在阅读完第三部分中与 Class 相关的内容之后,再去详细阅读以下官方文档:

在写程序的过程中,为别人(和将来的自己)写注释、写 Docstring;在写程序的过程中,为了保障程序的结果全面正确而写测试;或者干脆在最初写的时候就考虑到各种意外所以使用试错语句块 —— 这些明明是天经地义的事情,却是绝大多数人不做的…… 因为感觉有点麻烦。

等你把整本书都完成之后,记得回来再看这个链接:

收获总结:

模块这一节,之前没有看得很明白,只是机械地参照示例写代码,没搞清楚不同形式之间的本质区别,比如from...import...和import...。可能重复的不够吧,今天终于看到“关键字”了,豁然开朗。

Daliy Tips:

  1. 模块

可以被外部调用的 .py 文件,有个专门的称呼,模块(Module)

  1. 模块文件系统目录检索顺序

Python 会按照以下顺序去寻找:

  • 先去看看内建模块里有没有你所指定的名称;
  • 如果没有,那么就按照 sys.path 所返回的目录列表顺序去找。

可以通过以下代码查看你自己当前机器的 sys.path:

import sys sys.path

在 sys.path 所返回的目录列表中,你当前的工作目录排在第一位。

有时,你需要指定检索目录,因为你知道你要用的模块文件在什么位置,那么可以用 sys.path.append() 添加一个搜索位置

import sys sys.path.append("/My/Path/To/Module/Directory") import my_module

  1. 系统内建的模块

可以用以下代码获取系统内建模块的列表:

import sys ​ sys.builtin_module_names "_sre" in sys.builtin_module_names # True "math" in sys.builtin_module_names # False (根据自己电脑库的安装情况,结果会有不同)

跟变量名、函数名,不能与关键字重名一样,你的模块名称也最好别与系统内建模块名称重合。

  1. 引入指定模块中的特定函数
  • from mycode import * # mycode.is_prime()
  • from mycode import is_prime # 直接写 is_prime()

注意,如果当前目录中并没有 mycode.py 这个文件,那么,mycode 会被当作目录名再被尝试一次 —— 如果当前目录内,有个叫做 mycode 的目录(或称文件夹)且该目录下同时要存在一个 init.py 文件(通常为空文件,用于标识本目录形成一个包含多个模块的 包(packages),它们处在一个独立的 命名空间(namespace)),那么,from mycode import * 的作用就是把 mycode 这个文件夹中的所有 .py 文件全部导入……

如果想要导入 foo 这个目录中的 bar.py 这个模块文件,那么,可以这么写:

import foo.bar

或者

from foo import bar

  1. 引入并使用化名
  • 可以为引入的函数设定 化名(alias),而后使用化名调用函数
  • 甚至干脆给整个模块取个化名

from mycode import is_prime as isp isp(3)

import mycode as m

m.is_prime(3) m.say_hi('mike', 'zoe')

  1. 模块中不一定只有函数

一个模块文件中,不一定只包含函数;它也可以包含函数之外的可执行代码。只不过,在 import 语句执行的时候,模块中的非函数部分的可执行代码,只执行一次。

  1. dir() 函数

可以用 dir() 函数查看模块中可触达的变量名称和函数名称

import mycode dir(mycode)

  • 姓名:Galaxy
  • 学习时间:20190823
  • 学习内容:2.4.4~2.4.5 递归函数及函数文档
  • 学习用时:3h

收获总结:

  1. 递归的适用场景是:可以把复杂的问题分解为相同版本更简单的问题。但是,有时递归并非是最高效的算法。MIT Python公开课里面举例讲解,计算斐波纳契数列,用字典就要比用递归效率高很多。用字典可以追踪中间值,捕获信息。

  2. 这段时间,通过不同途径学习Python,终于亲身体会到,学习一项技能,不能只依赖一本书,因为每本书都有其侧重点,限于主题和篇幅,也都不可能做到非常全面。所以,就需要通过多渠道学习来达到查漏补缺、全面掌握相关知识的目的。

Daliy Tips:

  1. 递归函数的共同特征:
  • 在 return 语句中返回的是自身的调用(或者是含有自身的表达式)
  • 为了避免死循环,一定要有至少一个条件下返回的不再是自身调用……
  1. 变量的作用域
  • 在函数内部被赋值而后使用的,都是局部变量,它们的作用域是局部,无法被函数外的代码调用;
  • 在所有函数之外被赋值而后开始使用的,是全局变量,它们的作用域是全局,在函数内外都可以被调用。

定义如此,但通常程序员们会严格地遵守一条原则:

在函数内部绝对不调用全局变量。即便是必须改变全局变量,也只能通过函数的返回值在函数外改变全局变量

这个原则同样可以在日常的工作生活中 “调用”:

做事的原则:自己的事自己做,别人的事,最多通过自己的产出让他们自己去搞……

当一个变量被当做参数传递给一个函数的时候,这个变量本身并不会被函数所改变。

def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)

a = 5 b = factorial(a) # a 并不会因此改变; print(a, b) a = factorial(a) # 这是你主动为 a 再一次赋值…… print(a, b)

  1. 递归函数三原则
  • 根据定义,递归函数必须在内部调用自己;
  • 必须设定一个退出条件;
  • 递归过程中必须能够逐步达到退出条件……

当需要解决的问题,可以被逐步拆分成很多越来越小的模块,然后每个小模块还都能用同一种算法处理的时候,用递归函数最简洁有效。

def teach_yourself(anything): while not create(): learn() practice() return teach_yourself(another) teach_yourself(coding)

自学还真的就是递归函数呢……

  1. 思考与练习

普林斯顿大学的一个网页,有很多递归的例子

https://introcs.cs.princeton.edu/java/23recursion/

  1. 函数的文档 —— Docstring

Docstring 如若存在,必须在函数定义的内部语句块的开头,也必须与其它语句一样保持相应的缩进(Indention)。Docstring 放在其它地方不起作用

书写 Docstring 的规范

  1. 无论是单行还是多行的 Docstring,一概使用三个双引号扩起;
  2. 在 Docstring 内部,文字开始之前,以及文字结束之后,都不要有空行;
  3. 多行 Docstring,第一行是概要,随后空一行,再写其它部分;
  4. 完善的 Docstring,应该概括清楚以下内容:参数、返回值、可能触发的错误类型、可能的副作用,以及函数的使用限制等等;
  5. 每个参数的说明都使用单独的一行……

关于 Docstring,有两个规范文件:

Docstring 是写给人看的,所以,在复杂代码的 Docstring 中,写 Why 要远比写 What 更重要

  1. Sphinx 版本的 Docstring 规范

Sphinx 可以从 .py 文件里提取所有 Docstring,而后生成完整的 Documentation。将来若是你写大型的项目,需要生成完善的文档的时候,你就会发现 Sphinx 是个 “救命” 的家伙,省时、省力、省心、省命……

以下两个链接,放在这里,以便你将来查询:

sphinx.ext.napoleon – Support for NumPy and Google style docstrings sphinx.ext.autodoc – Include documentation from docstrings

  • 姓名:Galaxy
  • 学习时间:20190822
  • 学习内容:2.4.3 匿名函数以及参数的拓展阅读
  • 学习用时:3h

收获总结:

发现新大陆,xue.cn新上线的“学习计划”功能很炫酷,增加了部分章节的“拓展阅读”资料,可以起到很好的查漏补缺、答疑解惑的辅助作用,对初学者很友好,能够很快捷地厘清困惑。

Daliy Tips:

  1. 在什么样的情况下,要给一个函数取一个化名呢?

为函数取一个化名,应该是为了提高代码可读性

def _is_leap(year): return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

year_leap_bool = _is_leap year_leap_bool #<function main._is_leap(year)> year_leap_bool(800) # _is_leap(800) -> True

  1. lambda —— “匿名函数”

写一个很短的函数可以用 lambda 关键字。

lambda 的语法结构如下:

lambda_expr ::= "lambda" [parameter_list] ":" expression

注意:lambda 语句中,: 之后有且只能有一个表达式。这个表达式的值,就是这个函数的返回值。

可以直接这样使用:

(lambda x: 2 * x)(8)

也可以将其赋值给另一个变量,再由该变量来调用函数:

add = lambda x, y: x + y # 相当于是给一个没有名字的函数取了个名字 add(3, 5)

  • 匿名函数本质上是一个函数,没有函数名称,因此使用匿名函数不用担心函数名冲突;
  • 匿名函数一般适用于创建一些临时性的,小巧的函数;
  1. lambda 的使用场景

作为某函数的返回值

def make_incrementor(n): return lambda x: x + n

f = make_incrementor(42) f(0)

f(1)

作为某函数的参数

map(function, iterable, ...)

Return an iterator that applies function to every item of iterable, yielding the results.

def double_it(n): return n * 2

a_list = [1, 2, 3, 4, 5, 6]

b_list = list(map(double_it, a_list)) b_list

c_list = list(map(lambda x: x * 2, a_list)) c_list

pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] pairs.sort(key=lambda pair: pair[1]) pairs

sort(*, key=None, reverse=False)

key specifies a function of one argument that is used to extract a comparison key from each list element (for example, key=str.lower). The key corresponding to each item in the list is calculated once and then used for the entire sorting process. The default value of None means that list items are sorted directly without calculating a separate key value.

官方文档: https://docs.python.org/3/library/stdtypes.html?highlight=sort#list.sort

  • 姓名:Galaxy
  • 学习时间:20190821
  • 学习内容:2.4.1~2.4.2 关于参数
  • 学习用时:3h

收获总结:

位置参数、关键字参数、任意位置参数、任意关键字参数,如果一个函数里面包含所有这些类型的参数,要是再来点故意为之的干扰,脑子就成一锅粥了。这个需要在实践中不断刻意练习才能刻在脑子里,就像背单词一样,调用尽量多的注意力资源效果就是不一样。

Daliy Tips:

  1. Python 的 Keyword List:

    Python Keyword List and as assert async await break class continue def del elif else except False finally for from global if import in is lambda None nonlocal not or pass raise return True try while with yield

可以用以下代码查询关键字列表: ​

import keyword keyword.kwlist # 列出所有关键字 keyword.iskeyword('if') # 查询某个词是不是关键字

  1. 变量的作用域

全局变量(Global Variables)和局域变量(Local Variables)

  • 每次某个函数被调用的时候,这个函数会开辟一个新的区域,这个函数内部所有的变量,都是局域变量
  • 当外部调用一个函数的时候,准确地讲,传递的不是变量,而是那个变量的值
  • 如果传递进来的值是可变容器(比如,列表),那么在函数内部对其操作之前,先创建一个它的拷贝
  1. 可以接收一系列值的位置参数

如果在定义参数的时候,在一个位置参数(Positional Arguments)前面标注了星号,*,那么,这个位置参数可以接收一系列值,在函数内部可以对这一系列值用 for ... in ... 循环进行逐一的处理

  • 带一个星号的参数,英文名称是 “Arbitrary Positional Arguments”,姑且翻译为 “随意的位置参数”。
  • 带两个星号的参数,英文名称是 “Arbitrary Keyword Arguments”,姑且翻译为 “随意的关键字参数”。

def say_hi(*names): for name in names: print(f'Hi, {name}!')

say_hi('mike', 'john', 'zeo')

在函数内部,是把 names 这个参数当作容器处理的。而在调用函数的时候,是可以将一个容器传递给函数的 Arbitrary Positional Arguments 的 —— 做法是,在调用函数的时候,在参数前面加上星号 *

def say_hi(*names): for name in names: print(f'Hi, {name}!')

names = ('mike', 'john', 'zeo') say_hi(*names)

a_dictionary = {'ann':2321, 'mike':8712, 'joe': 7610} say_hi(*a_dictionary)

...

在定义可以接收一系列值的位置参数时,建议在函数内部为该变量命名时总是用复数

注意:一个函数中,可以接收一系列值的位置参数只能有一个;并且若是还有其它位置参数存在,那就必须把这个可以接收一系列值的位置参数排在所有其它位置参数之后。

def say_hi(greeting, *names): for name in names: print(f'{greeting}, {name.capitalize()}!')

say_hi('Hello', 'mike', 'john', 'zeo')

  1. 为函数的某些参数设定默认值

可以在定义函数的时候,为某些参数设定默认值,这些有默认值的参数,又被称作关键字参数(Keyword Arguments)。从这个函数的 “用户” 角度来看,这些设定了默认值的参数,就成了 “可选参数”。

def say_hi(greeting, *names, capitalized=False): for name in names: if capitalized: name = name.capitalize() print(f'{greeting}, {name}!')

say_hi('Hello', 'mike', 'john', 'zeo') say_hi('Hello', 'mike', 'john', 'zeo', capitalized=True)

  1. 可以接收一系列值的关键字参数(Arbitrary Keyword Argument)

def say_hi(**names_greetings): for name, greeting in names_greetings.items(): print(f'{greeting}, {name}!')

say_hi(mike='Hello', ann='Oh, my darling', john='Hi')

a_dictionary = {'mike':'Hello', 'ann':'Oh, my darling', 'john':'Hi'} say_hi(**a_dictionary)

say_hi(**{'mike':'Hello', 'ann':'Oh, my darling', 'john':'Hi'})

def add(**kwargs): return kwargs add() # 没有参数,kwargs 为空字典 {} add(x=1) # x=1 => kwargs={'x': 1} {'x': 1} add(x=1, y=2) # x=1, y=2 => kwargs={'y': 2, 'x': 1} {'y': 2, 'x': 1}

  • kwargs 可以接收不定长度的键值对,在函数内部,它会表示成一个 dict。
  • 也可以使用 **kwargs 的形式来调用函数
  1. 函数定义时各种参数的排列顺序

def say_hi(*names, greeting='Hello', capitalized=False): for name in names: if capitalized: name = name.capitalize() print(f'{greeting}, {name}!')

say_hi('mike', 'john', 'zeo') say_hi('mike', 'john', 'zeo', greeting='Hi')

函数被调用时,面对许多参数,Python 需要按照既定的规则(即,顺序)判定每个参数究竟是哪一类型的参数:

Order of Arguments

  1. Positional
  2. Arbitrary Positional
  3. Keyword
  4. Arbitrary Keyword

官方文档: Arbitrary Argument Lists

def func(x, y, z=0, *args, **kwargs): print 'x =', x print 'y =', y print 'z =', z print 'args =', args print 'kwargs =', kwargs

a = (1, 2, 3) b = {'u': 6, 'v': 7} func(*a, **b) x = 1 y = 2 z = 3 args = () # 元组 kwargs = {'u': 6, 'v': 7} # 字典

  • 姓名:Galaxy
  • 学习时间:20190820
  • 学习内容:2.1 ~ 2.3 为什么从函数开始?
  • 学习用时:3h

收获总结:

  1. 还不熟悉如何用命令行运行程序,Google了一下,没能很简单地实现预想的效果,先放一放。

  2. Demo程序虽然短小,却很有料。设计相当精巧,回味无穷。

Daliy Tips:

  1. Python Demo 程序:

https://github.com/python/cpython/tree/master/Tools/demo

最起码把这其中的以下几个程序都精读一下,看看自己的理解能力:

  • beer.py Well-known programming example: Bottles of beer.
  • eiffel.py Python advanced magic: A metaclass for Eiffel post/preconditions.
  • hanoi.py Well-known programming example: Towers of Hanoi.
  • life.py Curses programming: Simple game-of-life.
  • markov.py Algorithms: Markov chain simulation.
  • queens.py Well-known programming example: N-Queens problem.
  1. 从结构化编程的角度来看,写函数的一个基本要求就是:
  • 完成一个功能;
  • 只完成一个功能;
  • 没有任何错误地只完成一个功能……

涉及的话题有:

  • 参数的传递
  • 多参数的传递
  • 匿名函数以及函数的别称
  • 递归函数
  • 函数文档
  • 模块
  • 测试驱动编程
  • 可执行程序

结构化编程的核心就是拆分任务,把任务拆分到不能再拆分为止 —— 当一个函数只完成一个功能的时候……

Demo:

import sys

n = 100 """ if sys.argv[1:]: n = int(sys.argv[1]) """

def bottle(n): if n == 0: return "no more bottles of beer" if n == 1: return "one bottle of beer" return str(n) + "bottles of beer"

for i in range(n, 0, -1): print(bottle(i), "on the wall,") print(bottle(i) + ".") print("Take one down, pass it around,") print(bottle(i-1), "on the wall.")

sys.argv 参数

「argv」是「argument variable」参数变量的简写形式,一般在命令行调用的时候由系统传递给程序。这个变量其实是一个List列表,argv[0] 一般是“被调用的脚本文件名或全路径”,这个与操作系统有关,argv[1]和以后就是传入的系统命令参数。比如脚本执行语句是:>>>> python using sys.args.py "whoami",那么我们使用sys.argv[1]获取的就是“whoami”这个参数;

sys.argv[] 是用来获取命令行参数的,sys.argv[0] 获取的内容是“脚本自身名称”,所以参数从1开始,获取的是执行命令内容

  • 姓名:Galaxy
  • 学习时间:20190819
  • 学习内容:1.5.7~1.7
  • 学习用时:3h

收获总结:

一份持续更新的笔记,其意义在于:一边学习一边顺手把日后可能需要回顾的信息有序地编排在一起,最终提炼出一份个性化的、完备的、可检索的知识点索引备忘录——可以高效锁定目标的得力工具。

Daliy Tips:

  1. 创建文件

最简单的方式就是用 Python 的内建函数 open()。 open(file, mode='r')

可用的 mode 有以下几种:

  • 'r' 只读模式
  • 'w' 写入模式(重建)
  • 'x' 排他模式 —— 如果文件已存在则打开失败
  • 'a' 追加模式 —— 在已有文件末尾追加
  • 'b' 二进制文件模式
  • 't' 文本文件模式(默认)
  • '+' 读写模式(更新)

f = open('/tmp/test-file.txt', 'w') print(f.name) f.close()

  1. 删除文件

删除文件,就得调用 os 模块了。删除文件之前,要先确认文件是否存在,否则删除命令会失败。

import os

f = open('/tmp/test-file1.txt', 'w') print(f.name) f.close() #关闭文件,否则无法删除文件 if os.path.exists(f.name): os.remove(f.name) print(f'{f.name} deleted.') else: print(f'{f.name} does not exist.')

  1. 读写文件

创建文件之后,可以用 f.write() 把数据写入文件,也可以用 f.read() 读取文件。

文件有很多行的时候,

  • 可以用 file.readline() 操作,这个 Method 每次调用,都会返回文件中的新一行
  • 可以使用 file.readlines() 这个命令,将文件作为一个列表返回,列表中的每个元素对应着文件中的每一行
  • 可以用 file.writelines() 把一个列表写入到一个文件中,按索引顺序(从 0 开始)逐行写入列表的对应元素
  1. with 语句块

针对文件操作,Python 有个另外的语句块写法,更便于阅读:

with open(...) as f: f.write(...) ...

这样,就可以把针对当前以特定模式打开的某个文件的各种操作都写入同一个语句块了 用 with 语句块的另外一个附加好处就是不用写 file.close() 了

  1. 如何从容应对 “过早引用”

“只字不差地阅读” 是一项专门的技能。这是一种需要多次练习、长期训练才能真正掌握的技能。绝对不像听起来那么简单。

“就算读不懂也要读完” 的更高境界,是 “就算不明白也要先记住”。

反复做整理归纳总结,记不住才怪呢!

先关注使用再研究原理 不管怎么样,先用起来,只要开始用起来,理解速度就会加快 —— 实践出真知。

PEP,Python Enhancement Proposals https://www.python.org/dev/peps/pep-0008/

  1. 第一查询对象只能是官方文档
  • site:python.org
  • bytes site:python.org/3/library

习题:

给定一个数组和一个目标数,找出数组中的两个数下标

使其满足这两个数的和等于目标数 比如给出数组:[2, 7, 11, 15], 目标数9,因为 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1]

def find_index(nums, goal): i = 0 j = 0 for i, num1 in enumerate(nums):
for j, num2 in enumerate(nums): if i == j: continue elif num1 + num2 == goal: return [i, j]

nums = [2, 5, 3, 12, 17] goal = 17

print(find_index(nums, goal))

  • 姓名:Galaxy
  • 学习时间:20190816
  • 学习内容:1.5.6~数据容器-习题
  • 学习用时:4h

收获总结:

这道题含金量好大哦,苦思冥想了好久都没搞定,后来去xue.cn的习题讨论区看了看其他同学的思路。有一位同学lilasxie的方案很惊艳,真心佩服,真是做到融会贯通了,考虑很周全,感觉每一个知识点都用得恰到好处,堪称完美。同时也映衬出我对知识点的掌握不够灵活,有些场景根本想不起来可以去使用某个贴切的元素或者方法。多练、多练、多练,多借鉴,自学是一种社交活动,积极地开阔眼界、拓展思路远比一个人死磕高效得多。不止学习英语需要“浸入式”,学习编程更需要“浸入式”,以此慢慢积累那种手到擒来的“本能反应”。

Daliy Tips:

通过今天的习题,深深地感受到用for循环直接迭代数据容器里面的元素,这种用法有多么强大。思维惯性还是拉着我按C语言的方式思考,该走出来了。

习题:

  1. 给定一个数组,按照每个元素的所有位数之和来进行排序

比如([19, 45, 34,76,100,43] 排序后应为[100, 19],因为 1 + 9 > 1 + 0 + 0)

def sort_by_sum_of_digits(a_list): b_list = [str(x) for x in a_list]

c_list = []
for s in b_list:
    sum = 0
    for c in s:
        sum += int(c)
    c_list.append(sum)

d_list = sorted(zip(c_list, a_list))

result = []
for t in d_list:
    result.append(t[1])
return result

a_list = [19, 45, 34, 76, 100, 43] print(sort_by_sum_of_digits(a_list))

  • 姓名:Galaxy
  • 学习时间:20190812
  • 学习内容:1.5.6~数据容器-习题
  • 学习用时:100min

收获总结:

今天解决了昨天的遗留问题,做了两道习题。百思不得其解的表象,加上鬼使神差的不明运行结果,与其一个人纠结痛苦,不如与战友讨论,果然自学是一种社交活动,分分钟热心的战友们就让真相大白了,虽说可以暂时搁置,但是即刻释怀的愉悦岂不更好。

习题:

  1. 给定数组,输出一个字典,键为数组中每个元素的各位数的和,值为数组中的下标

[56, 45, 576, 899, 12, 100]

def sum_digit(num): sum = 0 list0 = list(str(num)) for j in range(len(list0)): sum += int(list0[j]) return sum

a_list = [56, 45, 576, 899, 12, 100] a_dict = {} for i in range(len(a_list)): a_dict.setdefault(sum_digit(a_list[i]), i) print(a_dict)

  1. 求集合交集、并集、差集

(1, 3, 5) 和 (3, 7, 9)

a = {1, 3, 5} b = {3, 7, 9}

print(a.intersection(b)) print(a.union(b)) print(a.difference(b))

  • 姓名:Galaxy
  • 学习时间:20190811
  • 学习内容:1.5.6~数据容器-练习、习题
  • 学习用时:2.5h

收获总结:

豪不偷懒地亲手敲一遍代码,才能发现眼过千遍都发现不了的、却自以为是地以为自己都懂了的细节。事实证明有效的方法论一定要严格执行。

Daily Tips:

  1. 迭代的同时获取索引

需要同时得到有序容器中的元素及其索引,可以调用 enumerate() 函数

s = 'Python' for i, c in enumerate(s): print(i, c)

for i, v in enumerate(range(3)): print(i, v)


L = ['ann', 'bob', 'joe', 'john', 'mike'] for i, L in enumerate(L): print(i, L)

t = ('ann', 'bob', 'joe', 'john', 'mike') for i, t in enumerate(t): print(i, t)


seasons = ['Spring', 'Summer', 'Fall', 'Winter'] list(enumerate(seasons)) [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] list(enumerate(seasons, start=1)) [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

  1. 同时迭代多个容器

chars = 'abcdefghijklmnopqrstuvwxyz' nums = range(1, 27) for c, n in zip(chars, nums): print(f"Let's assume {c} represents {n}.")

  • 姓名:Galaxy
  • 学习时间:20190810
  • 学习内容:1.5.6~数据容器-列表相关的练习、习题
  • 学习用时:2h

收获总结:

近期学习模式:把某一部分的所有内容先复习一遍,第二天再单独把所有代码敲一遍,然后再做几道习题,那些零散的、容易出错的知识点通过各种形式的不断重复,逐渐巩固下来,脑海中的整个知识结构也随之越来越完整、全面。

习题:

给定数组,输出所有小于 100 的值

[56, 45, 576, 899, 12, 56, 100]

for i in [56, 45, 576, 899, 12, 56, 100]: if i < 100: print(i)

  • 姓名:Galaxy
  • 学习时间:20190809
  • 学习内容:1.5.6~数据容器-字典
  • 学习用时:2h

Daily Tips:

  1. 字典(Dictionary)

Map 是容器中的单独一类,映射(Map)容器。映射容器只有一种,叫做字典(Dictionary)。

  • 字典直接使用 key 作为索引,并映射到与它匹配的 value
  • 在同一个字典里,key 都是唯一的。如果其中有重复的 key 的话,就跟 Set 那样会 “自动去重” —— 保留的是众多重复的 key 中的最后一个 key:value

字典的生成

aDict = {} bDict = {'a':1, 'b':2, 'c':3}

更新某个元素

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585} phonebook1['joe'] = 5802

添加元素

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585} phonebook2 = {'john':9876, 'mike':5603, 'stan':6898, 'eric':7898}

phonebook1.update(phonebook2)

删除某个元素

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}

del phonebook1['ann']

逻辑操作符

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}

'ann' in phonebook1 phonebook1.keys() phonebook1.values() phonebook1.items() ('stan', 6898) in phonebook1.items()

内建函数

  • len(phonebook1)
  • max(phonebook1)
  • min(phonebook1)
  • list(phonebook1)
  • tuple(phonebook1)
  • set(phonebook1) # 字典跟集合一样,自动去重,set()不仅将字典转换为集合类型,顺便还进行了排序
  • sorted(phonebook1)
  • sorted(phonebook1, reverse=True)

Methods

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585} phonebook2 = {'john':9876, 'mike':5603, 'stan':6898, 'eric':7898}

phonebook3 = phonebook2.copy() phonebook3.clear()

p = phonebook1.popitem() p = phonebook1.pop('adam', 3538) p = phonebook1.get('adam', 3538) p = phonebook1.setdefault('adam', 3538)

pop(key[, default]) If key is in the dictionary, remove it and return its value, else return default. If default is not given and key is not in the dictionary, a KeyError is raised.

  1. 迭代各种容器中的元素

迭代的同时获取索引

想同时得到有序容器中的元素及其索引,可以调用 enumerate() 函数

t = ('ann', 'bob', 'joe', 'john', 'mike') for i, t in enumerate(t): print(i, t)

迭代前排序

可以用 sorted() 和 reversed() 在迭代前先排好序

t = ('bob', 'ann', 'john', 'mike', 'joe') for i, t in enumerate(sorted(t)): print(i, t)

t = ('bob', 'ann', 'john', 'mike', 'joe') for i, t in enumerate(sorted(t, reverse=True)): print(i, t)

t = ('bob', 'ann', 'john', 'mike', 'joe') for i, t in enumerate(reversed(t)): print(i, t)

同时迭代多个容器

可以在 zip() 这个函数的帮助下,同时迭代两个或者两个以上的容器中的元素(多个容器中的元素数量最好相同)

chars = 'abcdefghijklmnopqrstuvwxyz' nums = range(1, 27) for c, n in zip(chars, nums): print(f"Let's assume {c} represents {n}.")

迭代字典中的元素

phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}

for key in phonebook1: print(key, phonebook1[key])

for key, value in phonebook1.items(): print(key, value)

  1. 参考链接

https://docs.python.org/3/tutorial/datastructures.html#dictionaries

https://docs.python.org/3/library/stdtypes.html#typesmapping

  • 姓名:Galaxy
  • 学习时间:20190808
  • 学习内容:1.5.6~数据容器-元组、集合
  • 学习用时:2h

收获总结:

对于Tuple和List的区别,每一遍阅读的时候都感觉特别容易搞糊涂,很难条理清楚地在脑海中复盘。所以今天再看完一遍,我除了在本子上图文并茂地总结它们的异同,还把能够提供理论支撑的简明“论据”放在旁边,形象与理性结合,就像左右脑协同工作一样,顷刻混沌变清明。

Daily Tips:

  1. 元组(Tuple)

    元组和列表的主要区别

    • List 是可变有序容器,Tuple 是不可变有序容器

    • List 用方括号标识 [],Tuple 用圆括号 标识 ()。

    • 创建一个元组的时候,用圆括号: a = ()。创建一个含多个元素的元组,可以省略这个括号。

    • 创建单个元素的元组,无论是否使用圆括号,在那唯一的元素后面一定要补上一个逗号 ,

    • 元组是不可变序列,所以,没办法从里面删除元素。但是,可以在末尾追加元素。所以,对元组来讲,“不可变” 的意思是说,“当前已有部分不可变”……

    • 首先是使用场景,在将来需要更改的时候,创建 List ;在将来不需要更改的时候,创建 Tuple。

    • 其次,从计算机的角度来看,Tuple 相对于 List 占用更小的内存

    • range() 函数返回的等差数列就是一个 Tuple —— range(6) 就相当于 (0, 1, 2, 3, 4, 5)。

  2. 集合(Set)

    • 集合与列表不同的地方在于,首先它不包含重合元素,其次它是无序的;进而,集合又分为两种,Set,可变的,Frozen Set,不可变的。

    创建

    • 创建空集合的时候,必须用 set(),而不能用 {}
a = {}    # 注意这样创建的是一个 dict,而不是 set
b = set() # 这样创建的才是空集合
- 也可以将序列数据转换(Casting)为集合。转换后,返回的是一个**已去重**的集合。

- Set 当然也可以进行 Comprehension
a = "abcabcdeabcdbcdef"
b = {x for x in a if x not in 'abc'}

**操作**

- 将序列类型数据转换成 Set,就等于**去重**。
- 可以用 in 来判断某个元素是否属于这个集合。
- len()、max()、min(),也都可以用来操作 Set,但 del 却不行 —— 因为 **Set 中的元素没有索引(它不是有序容器)**。
- 从 Set 里删除元素,得用 set.remove(elem);而 Frozen Set 是不可变的,所以不能用 set.remove(elem) 操作。

操作符:
- 并集:|
- 交集:&
- 差集:-
- 对称差集:^
admins = {'Moose', 'Joker', 'Joker'}
moderators = {'Ann', 'Chris', 'Jane', 'Moose', 'Zero'}

admins                 # 去重自动完成
admins ^ moderators    # admins 和 moderator 中不是身兼两职的都有谁?in admins or moderator but not both
操作符对应的 Methods :
  意义	   操作符	    Methods	                      Methods 相当于
- 并集	     |	 set.union(*others)	                set | other | ...
- 交集	     &	 set.intersection(*others)	        set & other & ...
- 差集	     -	 set.difference(*others)	        set - other - ...
- 对称差集	 ^	 set.symmetric_difference(other)    set ^ other

**逻辑运算**

- set == other
- set != other
- isdisjoint(other)

True: set 与 other 非重合;即,set & other == None

- issubset(other),set <= other
- set < other
- issuperset(other),set >= other
- set > other

**更新**

更新集合的 Method:
- add(elem)
- remove(elem)
- discard(elem)
- pop(elem)
- clear()
- set.update(*others)
- set.intersection_update(*others)
- set.difference_update(*others)
- set.symmetric_difference_update(other)

**冻结集合**

- Frozen Set 之于 Set,正如 Tuple 之于 List,前者是不可变容器(Immutable),后者是可变容器(Mutable),无非是**为了节省内存**使用而设计的类别。
  • 姓名:Galaxy
  • 学习时间:20190807
  • 学习内容:1.5.6~数据容器-列表
  • 学习用时:2h

收获总结:

字符串和列表的内容都比较多,看的过程中只求弄明白眼前的单个知识点,看完、练完,再对照老师整理的表格,真的是瞬间感觉理解与记忆零压力。

Daily Tips:

  1. 列表的生成

    生成一个列表,有以下几种方式:

    • a_list = []
    • b_list = [1, 2, 3]
    • list(), or list(iterable) # 这是 Type Casting
    • [(expression with x) for x in iterable] # List Comprehension
  2. 列表的操作符

    • 拼接:+(与字符串不一样的地方是,不能用空格 ' ' 了)
    • 复制:*
    • 逻辑运算:in 和 not in,<、<=、>、>=、!=、==
  3. 根据索引提取列表元素

    • 列表是可变序列,所以,不仅可以提取,还可以删除,甚至替换。
    • 列表(List)是可变序列,而字符串(str)是不可变序列,所以,对字符串来说,虽然也可以根据索引提取,但没办法根据索引删除或者替换
  4. 列表可用的内建函数

    • len()
    • max()
    • min()
  5. Methods

    • 列表是可变类型(Mutable type),所以,它最起码可以被排序 —— 使用 sort() Method: 被比较的元素应该是同一类型 —— 所以,不是由同一种数据类型元素构成的列表,不能使用 sort() Method。

    • 可变序列还有一系列可用的Methods: a.append(),a.clear(),a.copy(),a.extend(t),a.insert(i,x),a.pop(i),a.remove(x),a.reverse()……

    • a.append() # 在末尾追加一个元素

    • a.extend(t) # 在末尾追加一个列表

    • a.reverse() # 只对当前序列操作,并不返回一个逆序列表;返回值是 None

    演示拷贝 .copy() 与赋值 = 的不同 (# 对一个拷贝操作,不会更改 “原件”)

e_list = d_list del e_list[6:8] print(e_list) print(d_list) # 对 e_list 操作,相等于对 d_list 操作

- a.remove(x)    # 去除 'example' 这个元素,如果有多个 'example',只删除第一个
- a.pop(i)       # pop() 删除并返回被删除的值
  • 姓名:Galaxy
  • 学习时间:20190806
  • 学习内容:1.5.5~字符串之发现、探索……
  • 学习用时:4h

收获总结:

  1. 每多看一遍都必然有新的收获,今天以练为主,居然意外收获了很多不动手就找不到的小宝藏。寻宝光靠眼睛可不够,多么深刻的领悟!

  2. 发现疑惑要么用代码测试,要么用Google搜集信息,以各种方式查漏补缺,最终全面、完整地掌握一门技能,在这个过程中逐渐积累自学的小窍门。

Daily Tips:

  1. 字符串与数值之间的转换

    • 数字构成的字符串,可以被转换成数值,转换整数用 int(),转换浮点数字用 float()。
    • 与之相对,用 str(),可以将数值转换成字符串类型。

    关于字符串与数值之间的转换,直到昨天我都还没彻底理清,只是觉得自然而然。每当有这种“原来如此”的慨叹时,就说明真的get了。再也不会混淆了。

  2. 3个单引号及3个双引号的特别之处

    • 用单引号或者双引号定义一个字符串的时候只能把字符串连在一起写成一行,如果非要写成多行,就得在每一行后面加一个\表示连字符。而且即使这样写也不能得到期望的输出。这就是3个引号的作用了。

    • 虽然也可以通过给字符串加上\n实现,但是这样在输入的时候看起来就乱了很多。所以这种情况下尽量使用3个引号,只需要注意如果字符串中包含有单引号就要使用双引号来定义。

    • 而且使用3个引号还有一个特别棒的作用就是:加注释!

print(
"""
Simple is better than complex.
Complex is better than complicated.
"""
) 
  1. 处理字符串的 Method

大小写转换

通过尝试发现**专门针对行首字母大写**的  str.capitalize() 和**针对每个词的首字母大写**的 str.title()这两个method的行为方式跟我预想的又不一样,如果不是随意尝试偶然发现意料之外的结果,我会一直想当然地认为:str.capitalize()只作用于句首的那一个字符;str.title()只作用于每个单词的首字母。然而,事实是:这两个method给出的结果很专业,它们除了确保应该大写的那些字符大写之外,其它的字符统统变成小写,无论它们原来是什么形式。
'nOW iS bETTER tHAN nEVER.'.title()
Out[13]: 'Now Is Better Than Never.'

'nOW iS bETTER tHAN nEVER.'.capitalize()
Out[15]: 'Now is better than never.'

字符串排版

for i in range(1, 11):
    print(str(i))       # 测试代码
    filename = str(i).zfill(3) + '.mp3'
    print(filename)
这段代码里面的str(i)令我迷惑,我一直费力地将它理解为代表文件名的字符串变量,直到一个字符一个字符地敲代码,感觉还是不对劲,想着要是不去规范文件名,那这些文件名会是什么样子,不妨加一行测试代码试试看?看到运行结果立刻恍然大悟,原来str(i)在这里是当内建函数用的,而不是代表文件名的变量,就算是,至少也应该长成这样str[i]。有时候,我觉得自己的思维方式很奇特!
  • 姓名:Galaxy
  • 学习时间:20190805
  • 学习内容:1.5.5~字符串
  • 学习用时:3h

收获总结:

这一节内容很多,分两天才看完,也只是用眼睛过了一遍。想要轻松回顾重点可不容易,打算再花一天时间,按照老师的建议,把所有代码亲自敲一遍,然后对照表格回忆知识点,反复多次直到牢记。

Daily Tips:

  1. 处理字符串的内建函数

把字符串当做处理对象的有:ord()、input()、int()、float()、len()、print()。

  1. 处理字符串的 Method

大小写转换

转换字符串大小写的是 str.upper()、str.lower() 和 str.swapcase(),以及 str.casefold();另外,还有专门针对行首字母大写的  str.capitalize() 和针对每个词的首字母大写的 str.title()

'Now is better than never.'.upper()

# 在 Python 命令行工具之中,单个下划线,是个特殊变量;
# 保存着最近的语句或者表达式的结果
# 上一个 Cell 执行过后,_ 中保存着 'NOW IS BETTER THAN NEVER.'

_.lower()

# casefold() 也是转换成小写,但它能处理更多欧洲语言字符

另外,还有个 str.encode() 在处理非英文字符串(比如中文)的时候,经常会用到

s = '简单优于复杂!'
s.encode()

搜索与替换

str.count() 搜寻子字符串出现次数
str.count(sub [,start=0[, end=len(str)]])

str.find(), str.rfind(), str.index()

# str.find(sub[, start[, end]])
# str.rfind(sub[, start[, end]])
# rfind() 返回最后 sub 出现的那次的位置;find()是最早的那次。没有找到就返回 -1

# str.index(sub[, start[, end]])
# str.rindex(sub[, start[, end]])
# 作用与 find() / rfind()相同,但如果没找到的话,会触发 ValueError 异常

str.startswith() 和 str.endswith() 是用来判断一个字符串是否以某个子字符串起始或者结束的

如果只想知道 “有没有”,而无需知道 “在哪里”,那么可以用 in , not in 操作符

str.replace()
str.replace(old, new[, count])

专门替换 TAB(\t)的 Method -- 把字符串中的 TAB(\t)替换成空格
str.expandtabs( tabsize=8)

去除子字符

str.strip([chars])

- 它最常用的场景是去除一个字符串首尾的所有空白,包括空格、TAB、换行符等等。
- 如果给定了一个字符串作为参数,那么参数字符串中的所有字母都会被当做需要从首尾剔除的对象,直到新的首尾字母不包含在参数中,就会停止剔除
- 还可以只对左侧处理,str.lstrip() 或者只对右侧处理,str.rstrip()

拆分字符串

str.splitlines()
- str.splitlines() 返回的是个列表(List),由被拆分的每一行作为其中的元素。

str.split()
- str.split(sep=None, maxsplit=-1),是将一个字符串,根据分隔符进行拆分,第二个参数指定拆分几次
- 如果没有给 str.split() 传递参数,那么默认为用 None 分割(各种空白,比如,\t 和 \r 都被当作 None)

str.partition()

拼接字符串

str.join()
- str.join(_iterable_)
- **The separator between elements is the string providing this method.**

s = '' t = ['P', 'y', 't', 'h', 'o', 'n'] s.join(t)

字符串排版

str.center(width[, fillchar])
- 将字符串居中放置 —— 前提是设定整行的长度,第 2 个参数可选,且只接收单个字符 

str.ljust(width),str.rjust(width)
- 将字符串靠左或者靠右对齐放置:

str.zfill(width)
- 将字符串转换成左侧由 0 填充的指定长度字符串。这在批量生成文件名的时候就很有用……

for i in range(1, 11): filename = str(i).zfill(3) + '.mp3' # str()是内建函数 print(filename)

格式化字符串

对字符串进行格式化,指的是将特定变量插入字符串特定位置的过程。常用的 Methods 有两个:

**str.format()**
- str.format(*args, **kwargs)

- 它的作用是:
- 在一个字符串中,插入一个或者多个占位符 —— 用大括号 {} 括起来;
- 而后将 str.format() 相应的参数,依次插入占位符中;占位符中可以使用由零开始的索引。

str.format() 里可以直接写表达式……

name = 'John' age = 25 '{} is a grown up? {}'.format(name, age >= 18)

**f-string**
- f-string 与 str.format() 的功用差不多,只是写法简洁一些 —— 在字符串标示之前加上一个字母 f

name = 'John' age = 25 f'{name} is {age} years old.' f'{name} is a grown up? {age >= 18}'

只不过,str.format() 的用法中,索引顺序可以任意指定,于是相对更为灵活

字符串属性

字符串还有一系列的 Methods,返回的是布尔值,用来判断字符串的构成属性

  • 姓名:Galaxy
  • 学习时间:20190804
  • 学习内容:1.5.5~字符串
  • 学习用时:160min

收获总结:

在1.5.4函数那一节遇到“输出字符串所有有数字出现的位置”这道习题时,不知道从何下手,甚至怀疑题意表达不清。于是先搁置下来了。今天看到字符串的索引这部分时,惊奇地发现,原来str.index()不止可以提取子串,它竟然也可以返回某个字符的具体索引位置,之前看的那几遍怎么就没发现呢?!带着问题学习有惊喜!

Daily Tips:

  1. 字符码表的转换

把单个字符转换成码值的函数是 ord(),它只接收单个字符;它返回该字符的 unicode 编码。与 ord() 相对的函数是 chr(),它接收且只接收一个整数作为参数,而后返回相应的字符。

  1. 字符串的标示

标示一个字符串,有 4 种方式,用单引号、用双引号,用三个单引号或者三个双引号。

  1. 字符串与数值之间的转换

由数字构成的字符串,可以被转换成数值,转换整数用 int(),转换浮点数字用 float()。

与之相对,用 str(),可以将数值转换成字符串类型。

int() 在接收字符串为参数的时候,只能做整数转换。

  1. input() 的功能是接收用户的键盘输入,而后将其作为字符串返回。

它可以接收一个字符串作为参数,在接收用户键盘输入之前,会把这个参数输出到屏幕,作为给用户的提示语。这个参数是可选参数,直接写 input(),那么它在要求用户输入的时候,就没有提示语。

  1. 转义符

有一个重要的字符,叫做 “转义符”,\,它本身不被当作字符,你要想在字符串里含有这个字符,得这样写 \:

转义符号 \ 的另外两个常用形式是和 t、n 连起来用,\t 代表制表符(就是用 TAB ⇥ 键敲出来的东西),\n 代表换行符(就是用 Enter ⏎ 敲出来的东西)。

所以,一个字符串,有两种形式,raw 和 presentation,在后者中,\t 被转换成制表符,\n 被转换成换行。

在写程序的过程中,我们在代码中写的是 raw,而例如当我们调用 print() 将字符串输出到屏幕上时,是 presentation

s = "He said, it\'s fine." # raw
print(s)                   # presentation
  1. 字符串的索引

字符串属于有序容器。字符串里的每个字符,对应着一个从 0 开始的索引。索引可以是负数

        0	1	2	3	4	5
        P	y	t	h	o	n
        -6	-5	-4	-3	-2	-1

str.index(sub[, start[, end]]) Like find(), but raise ValueError when the substring is not found.

s = 'Python'
for char in s:
    print(s.index(char), char)

字符串就是字符的有序容器

我们可以使用索引操作符根据索引提取字符串这个有序容器中的一个或多个元素,即,其中的字符或字符串。这个 “提取” 的动作有个专门的术语,叫做 “Slicing”(切片)。它最终可以写成以下 4 种形式:

- s[index] 
- s[start:] 
- s[start:stop] 
- s[:stop] 
- s[start:stop:step] 

习题:

输出字符串所有有数字出现的位置 “abc120dfg09”

s = "abc120dfg09" s0 = "0123456789" for char in s: if char in s0: print(s.index(char), char)

  • 姓名:Galaxy
  • 学习时间:20190803
  • 学习内容:1.5.4-函数
  • 学习用时:2h

收获总结:

今天做了三道习题,验证了从MIT Python学到的关于字符串类型的不可变(Immutable)特性。之前误以为不可变就是不可重新赋值,以致使用str.replace()替换子串的时候,每次都重新定义一个新的字符串变量来存储其返回值。经过MIT Python中对于字符串的再次学习,终于彻底搞清楚了。

string类型是不可变的,意味着它的值不能被修改;但是,可以通过为其重新绑定一个新的对象,替换原来的值。

strings are “immutable” – cannot be modified

s = "hello" s[0] = 'y' gives an error s = 'y'+s[1:len(s)] is allowed, s bound to new object

  • 姓名:Galaxy
  • 学习时间:20190802
  • 学习内容:1.5.4-函数
  • 学习用时:1.5h

收获总结:

由于xue.cn学习中心的已完成习题统计数目不对,今天花了不少时间配合程序员排查bug。这个bug解决到最后,居然也有我的贡献,帮我找到了自己代码中的bug。由于对题目要求理解错误,把原本写对的代码硬是给改出bug了,导致程序进入死循环,无法正常终止……幸亏有个反馈系统,错误才能及时暴露出来。

Daily Tips:

  1. 函数 print()最基本的功能:给它什么,它就把什么输出到屏幕上。

  2. print(*object, sep=' ', end='\n', file=sys.stdout, flush=False)

    print() 这个函数是可以往文件里写数据的,只要指定 file 这个参数为一个已经打开的文件对象就可以了

  3. sys.stdout 与 print

    当我们在 Python 中打印对象调用 print(obj) 时候,事实上是调用了 sys.stdout.write(obj+'\n')

    print 将你需要的内容打印到了控制台,然后追加了一个换行符

    print 会调用 sys.stdout 的 write 方法

    以下两行在事实上等价:

sys.stdout.write('hello'+'\n') print('hello')

  1. print() 这个函数的返回值是 None —— 注意,它向屏幕输出的内容,与 print() 这个函数的返回值不是一回事。

print(print(1))

  1. 在函数定义中,带有 = 的,即,已为其设定了默认值的参数,叫做 Keyword Arguments,其它的是 Positional Arguments。

  2. 位置参数,是 “由位置决定其值的参数”,被传递的值的意义就是由参数的位置决定的。

  3. 总结

    • 你可以把函数当作一个产品,而你自己是这个产品的用户;
    • 既然你是产品的用户,你要养成好习惯,一定要亲自阅读产品说明书;
    • 调用函数的时候,注意可选位置参数的使用方法和关键字参数的默认值;
    • 函数定义部分,注意两个符号就行了,[] 和 =;
    • 所有的函数都有返回值,即便它内部不指定返回值,也有一个默认返回值:None;
  • 姓名:Galaxy
  • 学习时间:20190801
  • 学习内容:1.5.3-流程控制
  • 学习用时:2.5h

收获总结:

做了两道习题,能犯的错误都犯了。一方面,说明题目出得精巧,对于容易轻视的易错点可以加深印象,举一反三;另一方面,也体会到测试及测试代码的重要性,测试用例要全方位覆盖才能发现所有想当然的错误。

Daily Tips:

  1. continue 语句将忽略其后的语句开始下次循环,而 break 语句将从此结束当前循环,开始执行循环之后的语句

  2. for 语句块还可以附加一个 else —— 这是 Python 的一个比较有个性的地方。

  3. 写嵌套的判断语句或循环语句的时候,经常需要先用 pass 占位,而后逐一突破。

  4. 可以用一个赋值符号分别为多个变量赋值 coin_user, coin_bot = 10, 10

  5. 有控制流,才能算得上是程序。

    • 只处理一种情况,用 if ...
    • 处理 True/False 两种情况,用 if ... else ...
    • 处理多种情况,用 if ... elif ... elif ... else ...
    • 迭代有序数据类型,用 for ... in ...,如果需要处理没有 break 发生的情况,用 for ... else ...
    • 其它循环,用 while ...
    • 与循环相关的语句还有 continue、break、pass
    • 函数从控制流角度去看其实就是子程序

习题:

  1. 获取输入,根据以下规则输出等级

    使用 input 获取输入

    • 60 分及以下为 E
    • 60 到 70 分(包含70分)为 D
    • 70 到 80(包含80分)为 C
    • 80 到 90(包含90分)为B
    • 90分以上为 A

while True: grade = int(input("Please input your yuer grade:")) if grade <= 60: print("E") elif grade <= 70: print("D") elif grade <= 80: print("C") elif grade <= 90: print("B") else: print("A") break

  1. 计算数组 [45, 576, 899, 12, 56, -100] 的最大最小以及平均数

def func_ave(a): b = a sum = 0 for i in range(0, len(b)): sum += b[i] return sum/len(b)

a_list = [45, 576, 899, 12, 56, -100] v_max = max(a_list) v_min = min(a_list) v_ave = func_ave(a_list)

print(f'The max is {v_max}, the min is {v_min}, the ave is {v_ave}.')

  • 姓名:Galaxy
  • 学习时间:20190731
  • 学习内容:《自学是门手艺》- 1.5.2 值及其相应的运算
  • 学习用时:1.5h

收获总结:

先把书读厚再读薄,试着借用作者的视角,通过归纳、总结,复现每一部分内容的知识结构。最后,还要能再次把书读厚。如果能做到像作者那样去思考,那么,头脑中提炼出的知识结构就可以随时还原,就像根据目录检索正文一样。这样,一本书就彻底消化吸收了。

Daily Tips:

  1. 这一章主要介绍了基础数据类型的运算细节。而除了基础数据类型,我们需要由它们组合起来的更多复杂数据类型。但无论数据的类型是什么,被操作符操作的总是该数据的值。

  2. 值是程序的基础成分(Building blocks)

常量的值就是它们字面所表达的值。 变量必须先赋值才能使用,即,要先把一个值保存到变量中,它才能在其后被运算。

在 Python 中每个函数都有返回值,默认返回 None

  1. 既然有不同类型的数据,它们就分别对应着不同类型的值。通常相同类型的值才能相互运算。在不得不对不同类型的值进行运算之前,总是要事先做 Type Casting(类型转换)。

  2. 函数就相当于各种事先写好的子程序,给它传递一个值,它会对其进行运算,而后返回一个值(最起码返回一个 None)。

  3. 每个变量或者常量,除了它们的值之外,同时还相当于有一个对应的布尔值。

  • 姓名:Galaxy
  • 学习时间:20190730
  • 学习内容:《自学是门手艺》1.5.1
  • 学习用时:1.5h

收获总结:

这一节的内容都很基础,以前学过、也用过。前前后后看过好几遍了,但是每每温故都会知新。这可能是重复过程中的自然变化吧,因为每一次相比前一次的起点都不再一样了。仿佛回到以前在学校自学的状态了,只是这本教材很特别,从学生的角度讲解知识,只要够细心,刨根问底的答案都在那儿了,根本不需要老师。只不过,你不可能一次性完整接收所有信息,每一次都会有那么一些过目却没走心的知识点,好像还没炼成火眼金睛,就是“看不见”。

既然教是最好的学,那么,如果自学时,以“教”为标准来挖掘信息,就能收获最大化了。

Daily Tips:

  1. 不断优化程序,提高效率,最终是程序员的工作,不是编程语言本身的工作。到最后,所有的工具都一样,效用取决于使用它的人。

  2. 有思想,能解决问题,是另外一门手艺 —— 需要终生精进的手艺。

  3. 赋值符号与操作符的连用

x += 1 相当于 x = x + 1

单等号 = 当作赋值符号,把双等号 == 当作逻辑判断的 “等于”

  • 姓名:Galaxy
  • 学习时间:20190729
  • 学习内容:《自学是门手艺》1.1~1.5.1
  • 学习用时:3h

收获总结:

  1. 配合训练营的进度看完一遍《自学是门手艺》,然后花了半个月学习了MIT Python,现在回过头来再重新学习《自学是门手艺》。虽然完完整整只算看了一遍,但是前面很多内容其实已经看过很多遍了,因为反复忘,无法连贯,所以反复回头。直到边看边做笔记,终于通关了。这次打算进入总结、归纳、整理、组织的阶段了。

  2. 1.5.1 这一节里面遇到二进制数11001000,想把它手工换算成十进制验证一下,居然懵了!原本以为这么基础的知识可以随取随用呢,一试才知道,大脑的那片分区早已被岁月格式化了,残留的那点记忆还不如彻底没有。于是,查查维基百科,重新补课。

其实,前几天就感觉有必要找出教科书翻翻,又懒得翻箱倒柜。今天搞不定进制转换,憋得脑袋发胀,想起来维基百科可能是更好的选择,不仅覆盖教科书内容,关键是可以通过搜索精准定位。可检索的百科全书,一本就够!

Daily Tips:

  1. 逻辑关系应该能用符号表示。

  2. 任何一个逻辑表达式都会返回一个布尔值

今天之前,我好像一直都没搞清楚逻辑运算和布尔运算到底有什么渊源,虽然会用,但是头脑里没有很清晰的概念。归纳、整理的过程,就是想办法梳理、对比、分辨相关知识点的脉络,在它们之间建立清晰且正确的联系,然后反复回顾,直至它们成为大脑的一部分。

Galaxy1227 avatar Jul 31 '19 15:07 Galaxy1227

已收录。

https://github.com/selfteaching-learning-notes/selfteaching-learning-notes.github.io/commit/d27622a9a5478422d46c66b52b804579b65bcaae

yingca1 avatar Aug 01 '19 02:08 yingca1

@realcaiying 你好,这个#4 issue是我目前正在持续更新的,MIT课程那个#17 issue我是一次性把所有笔记上传了。

Galaxy1227 avatar Aug 04 '19 05:08 Galaxy1227

close #17, open #4

yingca1 avatar Aug 06 '19 15:08 yingca1