写在前面

可能你早就已经听过了Python,没错,Python盛名天下,GvR老爷子20多年前创造的玩具语言,现在已经成了无敌工具语言,后端、人工智能、 GUI编程,Python几乎无孔不入。几乎所有的Linux发行版都会默认安装Python。

不仅如此,Python还有着极其简单的语法,有编程经验的同学只需要几个小时就能上手,写出传统的 “Hello World!” 只需要一行代码就可以实现!

这篇文章是我的python学习笔记,部分内容是由ai生成,通过本文的阅读可以掌握Python编程的基础和一些高级技巧,为进一步深入学习和实际应用打下坚实的基础😃

更加详细的内容可以看这里,这是本人学习python的笔记。

Hello world

现在我们来编写第一个python程序 ,首先我们使用自己熟悉的编辑器,新建一个文件,保存为 hello.py,例如我是用 Vim:

vi hello.py

编写以下内容

print("Hello world!")

保存好,然后再终端中,输入python hello.py(这里填入的是该文件所在的路径),这样就可以看到输出了

$ python hello.py
Hello World!

基本类型

正如同大部分的编程语言都有整型、浮点数、字符串等等,Python当然也有。不过与其他语言,Python中的类型简单多了,也好用多了。如果你学过C、Java语言等等,就会知道,整型有 int, int8, int16… 好多种。

Python中可没有这么复杂,就一种:int。而浮点数也是这样,只有一种:float,字符串是 str。布尔类型是 bool。

除此之外,还有就是不那么常用的,复数(complex)。

首先我们在命令行输入 python,然后回车,接着我们输入数字1,字符串”Hello”,浮点数(就是我们平时说的小数),看看Python中他们分别长什么样

$ python
>>> 1
1
>>> "Hello"
'Hello'
>>> 1.234
1.234
>>> type(1)
<class 'int'>
>>> type("Hello")
<class 'str'>
>>> type(1.234)
<class 'float'>
>>>

在Python中,我们使用 type 来检测,或者说输出一个变量的类型。

为什么说Python中的变量比其他语言强大的多呢?举个例子,int8 之所以是 int8,是因为它的底层只有8个位,如果学过计算机组成 就知道,计算机里所有的东西,它的底层都是0或者1来表示的。一个0或者1就是一个位,英文是 bit。而 int8 只有8个bit,也就是说, 它最多只能表示 2 ** 8 = 256 个状态,或者说数字。那么超过这个表示范围的怎么办呢?答案是使用更多的bit来表示,例如 int16。

而Python里可不用这样,Python自动帮我们处理好了底层的一切,你看,无论是1,还是 99999999999999999999999999999999999999999, 他们的类型都是 int:

$ python
>>> type(99999999999999999999999999999999999999999)
<class 'int'>
>>> type(1)
<class 'int'>

这样子我们写代码的时候就方便很多,因为我们可以专注于我们真正要解决的问题,而不用管底层到底该用多少个bit来存储了😄。

变量

变量,顾名思义,它的值是可以变化的。变量名,就是一个名字,它代表着一个变量,比如 a = 1,a就是变量名,a在计算机内存里的 真正内容就是变量。不过实际上我们一般都会把 a 叫做变量 a,而不会严格去区分这两者的区别。举个例子,下面例子中的 a 就是个变量, 因为它的值一直在变化:

$ python
>>> a = None
>>> a = 1
>>> a = 2
>>> a = "Hello"
>>> a = 1.234
>>> a = None

容器类型

Python在标准库里提供了很多种数据结构,包括 dict, list, set, tuple。在 collections 这个库里还有更多的数据结构。接下来 我们讲解以下 dict, list, set 和 tuple。

所谓标准库,就是Python默认带的一些库,所谓的库,就是一堆Python代码作为一个合集来提供一些功能。

list

可能你用过其他语言中类似的数据结构,例如数组、链表等等,但是Python中的list与他们不太一样,list我们通常翻译为 列表。

首先Python中的列表是有顺序的,然后Python中的列表里可以包含任意东西,举个例子

In [1]: [1, "Hello", None, 3.14]
Out[1]: [1, 'Hello', None, 3.14]

可以看到,Python中的列表的表示,是在中括号里加入列表中的元素,而列表的元素没有类型要求,而且,Python中的列表是长度不固定 的,举个例子:

In [1]: a = [1, "Hello", None, 3.14]

In [2]: a.append("World")

In [3]: a
Out[3]: [1, 'Hello', None, 3.14, 'World']

In [4]: a.remove(1)

In [5]: a
Out[5]: ['Hello', None, 3.14, 'World']

In [6]: a[0]
Out[6]: 'Hello'

In [7]: a[-1]
Out[7]: 'World'

In [8]: a[:]
Out[8]: ['Hello', None, 3.14, 'World']

In [9]: a[1:]
Out[9]: [None, 3.14, 'World']

在上面的例子里,我们还展示了如何用下标来取Python中列表里的值,还有就是如何取其中的一部分,我们把这种操作叫做切片, 例如 a[:]是去全部,a[1:] 是取第一个元素及其后所有元素,a[0] 和 a[-1] 分别是取第一个和最后一个,如果下标是正数, 就是从左往右取值,而如果下标是负数,就是从右往左取值。

tuple

tuple 与 list 很多方面都是一样的,例如下标取值,切片等等,最大的区别在于,tuple是不可变的,也就是说,里面的元素 不可以替换,也不可以对tuple进行删除或者追加元素的操作:

In [1]: a = (1, "Hello", None, 3.14)

In [2]: a[0]
Out[2]: 1

In [3]: a[-1]
Out[3]: 3.14

In [4]: a[:]
Out[4]: (1, 'Hello', None, 3.14)

In [5]: a[1:]
Out[5]: ('Hello', None, 3.14)

In [6]: a.append("World")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-be23f54a6d34> in <module>
----> 1 a.append("World")

AttributeError: 'tuple' object has no attribute 'append'

那么为什么不直接使用list呢?答案是省内存,而且有的时候我们就是要保证元素的不可变。

tuple为什么比list更省内存呢?正是由于list的长度可变,为了容纳更多的元素,list需要在空间不够用的时候申请更多的 内存来保存元素,而申请内存是很慢的,因此一般的策略都是申请当前空间的两倍,也就是说,很有可能list里会有很多没有 用到,只是等待使用的空间,而tuple不需要,因为他的长度是不可变的,所以创建的时候是几个位置,就一直都是,因此tuple 比list 更加省内存。

dict

dict,很多编程语言里都有这个数据结构,他就是字典(其他语言一般叫哈希表、map)。它的作用也是把Key和Value关联起来,而 Python中的dict又比较特别了,所有的有 hash 方法的对象,都可以作为key,例如:

In [1]: a = {}

In [2]: a[None] = 1

In [3]: a[1] = 2

In [4]: a[2] = "hello"

In [5]: a["hello"] = 3.14

In [6]: class World:
   ...:     pass
   ...:

In [7]: a[3.14] = World

In [8]: a[World] = World()

In [9]: a
Out[9]:
{None: 1,
 1: 2,
 2: 'hello',
 'hello': 3.14,
 3.14: __main__.World,
 __main__.World: <__main__.World at 0x10c40b2b0>}

set

set 就是传说中的集合,Python中,一般用集合来进行各种集合操作,例如取交集,取并集。set 需要注意的是初始化方式与 dict 很容易搞混,如下:

In [1]: a = {}

In [2]: type(a)
Out[2]: dict

In [3]: b = {1}

In [4]: type(b)
Out[4]: set

In [5]: a = {1: 2}

In [6]: type(a)
Out[6]: dict

看到了吗?set 和 dict 都是使用大括号来初始化,如果给的值是空的或者键值对,那么就会被初始化为 dict,如果只给Key,那么 就会被初始化为 set。接下来我们看看 set 的常见用法:

In [1]: a = {1, 1, 2, 3, 4, 5, 6, 6, 8}

In [2]: b = {"Hello", "World", 3}

In [3]: 2 in a
Out[3]: True

In [4]: "World" in b
Out[4]: True

In [5]: "World" in a
Out[5]: False

In [6]: None in a
Out[6]: False

In [7]: a | b
Out[7]: {1, 2, 3, 4, 5, 6, 8, 'Hello', 'World'}

In [8]: a & b
Out[8]: {3}

In [9]: a
Out[9]: {1, 2, 3, 4, 5, 6, 8}

In [10]: b
Out[10]: {3, 'Hello', 'World'}

控制流

Python中的控制流与其他语言非常接近,所以不做赘述,接下来我们分别看几个例子:

if…elif…else

In [1]: def check_num(num):
   ...:     if num < 0:
   ...:         print("num < 0")
   ...:     elif num == 0:
   ...:         print("num == 0")
   ...:     else:
   ...:         print("num > 0")
   ...:

In [2]: check_num(0)
num == 0

while

In [3]: while True:
   ...:     print("infinite loop")

for

In [3]: for i in range(10):
   ...:     print(i)
   ...:
0
1
2
3
4
5
6
7
8
9

continue, break

这两者与其他语言一致,都是用于控制循环里的跳转。

最后,Python没有 switch 语句。

函数

Python中的函数使用 def 来进行声明。Python中的函数可以接受两种类型的参数:普通参数和命名参数(named arguments)。

In [1]: def check_num(num, ignore=False):
   ...:     if ignore:
   ...:         return
   ...:
   ...:     if num < 0:
   ...:         print("num < 0")
   ...:     elif num == 0:
   ...:         print("num == 0")
   ...:     else:
   ...:         print("num > 0")
   ...:

In [2]: check_num()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-e2de90bec545> in <module>
----> 1 check_num()

TypeError: check_num() missing 1 required positional argument: 'num'

In [3]: check_num(1)
num > 0

In [4]: check_num(2)
num > 0

In [5]: check_num(2, ignore=True)

In [6]: check_num(2, True)

如果 return 后面不接返回值,那么默认将会返回 None

面向对象编程

Python中使用 class 定义类,Python支持多继承。

In [1]: class Base:
   ...:     pass
   ...:

In [2]: class Children1(Base):
   ...:     pass
   ...:

In [3]: class Children2(Base):
   ...:     pass
   ...:

面向接口编程

Python中没有对接口的直接支持,但是实际上面向接口编程是一种编程思维、编程范式,与语言是否有关键字支持无关。Python中面向 接口编程我们一般使用 abc:

In [1]: import abc

In [2]: class Bird(abc.ABC):
   ...:     @abc.abstractmethod
   ...:     def fly(self):
   ...:         pass
   ...:

In [3]: class Parrot(Bird):
   ...:     pass
   ...:

In [4]: Parrot().fly()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-beb5d307db79> in <module>
----> 1 Parrot().fly()

TypeError: Can't instantiate abstract class Parrot with abstract methods fly

In [5]: class Parrot(Bird):
   ...:     def fly(self):
   ...:         print("flying")
   ...:

In [6]: Parrot().fly()
flying

可以看到,如果我们继承了 Bird 却没有实现接口的话,直接调用就会报错。

模块和包

如大多数语言一样,Python中也有包和模块的概念。

首先我们来说模块,Python中的一个模块,就是一个 .py 文件,模块的名字就是文件的名字,举个例子,我们有一个文件叫 sayhi.py, 它的内容是:

def sayhi():
    print("hi")

我们可以说,我们有个模块叫做 sayhi,这个模块里有个函数叫做 sayhi。

而包的概念,就是指把多个模块组合在一起,放在一个文件夹里,与此同时,这个包里一定要有一个 init.py 的文件,__init__.py 可以是空文件。举个例子,有这么一个包:

$ pwd
$ tree
.
├── __init__.py
├── sayhi.py

1 directory, 2 files

就是一个包,如果所在文件夹叫做 say,那么这个包的名字就是 say

异常处理

Python中的异常使用用法如下:

try:
    open("xxxxxxxxx")
except FileNotFoundError as e:
    print(e)

I/O处理

文件操作

在Python中,I/O操作是一个非常重要的部分,主要分为文件操作和网络操作。我们先来看文件操作。

读取文件

读取文件的操作非常简单,我们只需要使用 open 函数打开文件,然后使用 read 方法读取文件内容,最后记得关闭文件。下面是一个简单的示例:

# 打开文件
with open('example.txt', 'r') as file:
    # 读取文件内容
    content = file.read()

# 输出文件内容
print(content)

在这个例子中,我们使用 with 语句来打开文件,这样做的好处是,无论文件操作是否成功,都会自动关闭文件,避免资源泄露。

写入文件

写入文件也同样简单,我们只需要在打开文件时指定模式为写入模式 w 或追加模式 a,然后使用 write 方法将内容写入文件。示例如下:

# 打开文件
with open('example.txt', 'w') as file:
    # 写入内容
    file.write('Hello, world!')

# 追加内容
with open('example.txt', 'a') as file:
    file.write('\nAppending some text.')

网络操作

Python中进行网络操作一般使用 socket 库。以下是一个简单的TCP客户端示例:

import socket

# 创建一个 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接到服务器
s.connect(('www.example.com', 80))

# 发送请求
request = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
s.send(request.encode())

# 接收响应
response = s.recv(4096)

# 打印响应
print(response.decode())

# 关闭连接
s.close()

在这个例子中,我们创建了一个TCP连接到 www.example.com,发送了一个HTTP GET请求,并接收和打印了服务器的响应。

一个懒人