月初在图书馆借阅了《自学是门手艺》、《数据结构与算法·python语言实现》,断断续续看了四天多,今天开始正式撰写编程练习笔记,目前的最高目标就是把书本上的文字理解透,用自己的话表达出来。

基础篇

文章痕迹

学习计划

2024-1-21

学习了数据类型、分支循环

2024-1-28

  • 类的学习
  • 完善数据类型

2024-1-31

增加目录,完善变量与数据类型

函数调用

python是一种面向对象[1]的编程语言,有些特性与java很相似,其中一个极其相似的特征就是函数调用的语法。

Python可以通过使用的.(点操作符)来调用函数。例如我们现在有一堆数据(data)要进行排序(sort),我们便可以通过data.sort()来实现,其中点左侧的是被操作的对象,相当于自变量,右侧即所使用的方法,相当于函数

另外不值得注意的一些废话是:

  • 有些方法仅仅会返回对象的状态,例如数量、数据类型、最值、平均值、平方差等,并不改变其状态
  • 有些方法则会改变对象的状态,例如重新排序,更改大小写等等。

变量的命名规则

  • 变量名只能包含字母、数字和下划线。
  • 不能以数字开头
  • 不能与Python中关键字和函数名相同
  • 不能超过79个字符

潜规则:如果标识符名称以单个下划线开头,表示它是私有的,双下划线开头表示它是强私有的。

内存管理

栈:用来存储对象(如变量名等)用id来标识堆:用来存储对象的值。

特点:栈中值相同的对象会指向一个堆;栈的对象一旦调用结束(比如在一个函数运行过程中使用的中间变量)就会被移出栈,相应的在堆中储存的某些中间值也会被清空,从而来释放内存,仅将目标堆栈保存

那么如何认为判定该对象是中间变量或者已经没用了?Python采用了引用计数的方法:当一个对象被其他对象引用时,就会增加一个计数,不被引用时,就会减少一个计数,当其计数减小到零时,就会判定它已经没用了,下一波垃圾回收到来时,就会被抹除掉。

比如给一个值为666变量A赋值为999,那么原本存储666的内存就会失去引用,就会被回收。可以通过身份运算符来验证两个对象是否存在于内存的同一个部分:

  • is:如果两个操作数相同,则返回True
  • is not:如果两个操作数不相同,则返回True
内存地址测试
1
2
3
4
5
6
7
8
Before = 2077
print("Before_id=", id(Before))

Now = 2077
print("Now_id=", id(Now))

print("{0}与{1}的值是否相同?\n".format("Before", "Now"), Before is Now)

数据类型

字符串

类型 语法 例子 特点
字符串 'string'"string""""跨行字符串""" ‘bulabulabula’ 一系列字符

关于字符串的罗马文本处理,有以下几个常用函数:

  • title():单词首字母大写
  • upper():全部改大写
  • lower():全部改小写
  • f"{}":将花括号内的变量替换为其值
  • rstrip():删除字符末尾的空白
生命万岁
1
2
3
4
5
6
Song = 'viva la vida'

print("标题:", Song.title())
print("大写:", Song.upper())
print("小写:", Song.lower())
print("给你安利一首歌:", f"{Song.title()}")

类型 语法 例子 特点
字符串 number 666 一个整数
数据类型格式转换

先介绍下format()函数,用来替换字符串中的某些值。也是一个与LaTeX\LaTeX\newcommand命令极为相似的一个函数:

\newcommand
1
2
3
\newcommand{\youname}[2]{我是:#1 #2} % 定义带两个参数的命令

\WhoLWhat{秉}{蕑} % 带两个参数的命令的使用
format
1
print("我是{0}{1}".format("秉","蕑"))

列表

索引
类的试验
类的试验.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 21 13:22:12 2024
@author: sion
"""

# 类的试验
year = 2024
month = 1
day = 21
temperature = 15.3
week = 'Sunday'
weather = 'cloudy'
name = 'sion'
date = [year,month,day]
date_tuple = (year,month,day)
HUB = {date_tuple, name, week, weather, temperature}
# 布尔值
print('year的布尔值为:', bool(year))
print('name的布尔值为:', bool(name))
print('🫥空布尔值为:', bool())
print('\n')
# 整数型
print('temperature的整型值为', int(temperature))
print('🫥空整型值为:',int())
print('\n')

# 浮点型
print('month的浮点型值为', float(month))
print('year的浮点型值为', float(year))
print('🫥空浮点型值为:', float())
print('\n')

# 列表型
print('列表date的值为', list(date))
print('🫥空列表型的值为', list())
print('\n')

# set集合型
print('集合HUB的值为:', HUB)
OUTPUT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
year的布尔值为: True
name的布尔值为: True
🫥空布尔值为: False


temperature的整型值为 15
🫥空整型值为: 0


month的浮点型值为 1.0
year的浮点型值为 2024.0
🫥空浮点型值为: 0.0


列表date的值为 [2024, 1, 21]
🫥空列表型的值为 []


集合HUB的值为: {(2024, 1, 21), 'cloudy', 15.3, 'sion', 'Sunday'}

(假想)分数型

关于数据类型,主流的高级语言都有整型、浮点型、字符型、布尔型四大类,然而我们都知道,在计算的时候,约分为小数是有误差的,不管再怎么把浮点的精度提高,也无法保证绝对的精确,那么为何没有一种分式的数据类型呢?

我们就可以假想一个分数型数据,姑且用两个点来区分分子分母,比如用1..3表示13\frac{1}{3} ,这样一来可以把它看作占用来两个整型数据的空间。当计算数据的时候,就跟浮点与整数运算后变浮点一样,一旦有一个分数型数据参与计算,就需要整型变为k..1,浮点变为abcd...1000,全程使用分数进行运算。如有必要显示数值,则在将分数转化为易读的浮点类型或整数型。分数型的核心不可避免的是通分与约分,这种提高精度的方法势必会增加计算复杂度,虽然简单的加减法都需要通分一下,但涉及到乘除运算的时候,效率有时候还是蛮高的。

当未来某一时期,算力变得廉价时,这种牺牲步数而追求更高精度的方法也会被大量使用吧。

运算符

python的运算符非常口语化。比如:
与或非就是and or not等价就是is,不等就是is not;元素在序列中:in,不在序列中:not in

算术运算符中整式除法//与取模%运算很有意思,前者要满足得出的值为不大于商的最大整数(所以13=1)-\frac{1}{3}=-1)

自加运算与赋值相加是有略微的区别的。

分支与循环

据说利用分支和循环可以解决任意复杂度的流程控制,下面咱们来试试:

if语句

1
2
3
4
5
6
7
8
import random
year = random.randrange(1990,2025)
if year > 2023:
print('欢迎来到2024!穿越者!')
elif year < 2000:
print('恭喜,有一个千禧年在等着你!')
else:
print('欢迎来到中国飞速发展的二十年!')

While语句与for语句

if语句一般有两种思路来使用for循环,基于索引(是否包含),基于迭代(等差数列)。没啥好说的,直接来个练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 23 22:25:15 2024
Updated on Tue Jan 23 23:49:11 2024
Version: 0.0.1
Author: sion
Description: 求解倒班与星期的重合。一周七天,倒班六天,查找倒班休息日与周末重合的时间与天数
"""


print('🌄本程序可以计算三班倒(白白夜夜休休)与正常工作日休息时间的重合情况。')
days = int(input('⚪请输入要计算的天数:'))

# 用不可改变的元组来定义星期循环、倒班循环与格式化的汉字输出
weekcycle = (1, 2, 3, 4, 5, 6, 7)
workcycle = (1, 2, 3, 4, 5, 6)
datecn = ('一', '二', '三', '四', '五', '六', '日')

i = 0
j = int(input('⚪今天周几?'))-1
k = int(input('⚪倒班第几天?'))-1

for i in range(days):
# print(i, weekcycle[j], workcycle[k]) 调试语句
if (weekcycle[j] == 6 or weekcycle[j] == 7) and (workcycle[k] == 5 or workcycle[k] == 6):
print('从今天开始的第', i+1, '天,休息日重合,该天是周',
datecn[weekcycle[j]-1], ',倒班第', datecn[workcycle[k]-1], '天', sep='')
# 星期与倒班的计数循环
j += 1
if j > 6:
j = 0
k += 1
if k > 5:
k = 0

函数

在python中,通过def来定义函数,参数的数量在后面给出,不需要向tex一样命名参数的数量,比如我们定义一个名为count函数来计算某个数据出现的次数。

Conuting star🌠
1
2
3
4
5
6
7
8
9
10
11
12
13
stuff = ['star', 'money', 'star', 'money', 'money', 'money', 'star', 'money',]


def count(data, target):
n = 0
for item in data:
if item == target:
n += 1
return n


print(count(stuff, 'star'))

函数可以看作一个简单的盒子,一边输入,一边输出。在上个例子中,定义了count()函数,其输入为datatarget,输出为n。当执行到return语句时,函数就会结束执行,并返回输出值n。如果没有定义输出,就会返回一个None

疑问:没有输出的函数,意义何在呢?

疑问:自定义函数如何返回多个输出值?

疑问:为何有的函数括号里边儿是输入值,有的函数只在外边儿加个点儿就行了

类很像函数,但更为基础底层,是面向对象语言的特色。类中可以定义自己的函数,但是不叫做函数,而叫做方法,语法跟函数相比,只是在函数名前后添加了两对下划线:__count__

在_《Python编程:从入门到实践(第2版)》_中有这样一个例子:

定义一只小狗🐕
1
2
3
4
5
6
7
class Dog:
"""一次模拟小狗的简单尝试。"""

def __init__(self, name, age):
"""初始化属性name和age。"""
self.name = name
self.age = age

我们将方法 __init__() 定义成包含三个形参: selfnameage 。在这个方法的定义中,形参 self 必不可少,而且必须位于其他形参的前面。为何必须在方法定义中包含形参 self 呢?因为Python调用这个方法来创建 Dog 实例时,将自动传入实参 self 。每个与实例相关联的方法调用都自动传递实参 self它是一个指向实例本身的引用,让实例能够访问类中的属性和方法

面向对象的特点就是如此,先定义一个对象,然后赋予其各种属性。

类的特点

  • self是一个特别的参数,不传递任何值,相当于C语言中的指针;即使有一个函数不带参数,仍然需要在函数定义中提到self。
  • 类变量的特性:属于类本身,由类中的所有实例共享

继承

子类可以继承父类的特性,我们先创建一个父类

建立父类
1
2
3
class post()
def info(self)
print("这是一篇文章。")

算法篇

大O符号


  1. 什么是面向对象,我也不清楚啦 ↩︎