Python learning

本文详细介绍了Python的基础搭建、学习原因、版本差异、数据类型与结构,涵盖元组、列表、字典、集合、队列与栈、命名元组、迭代器、生成器、文件操作、进程与队列、线程与队列、哈希表、网络编程等内容,适合初学者快速上手。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 环境 搭建

Python文档下载地址:https://www.python.org/doc/
windows 安装 在这里插入图片描述
IDE : vscode pycharm
推荐使用 vscode

二 为什么要学习python

原因:人生苦短,我用python

三 python2 和python3 的区别

1 在python3 中 print 必须后面加()
2 在python3 中 input() 接收的用户输入均为str类型 ,在python2中 input接收到的值为 int,raw_input得到的为str类型
3 python2 中 range(1,4) 结果是列表 [1,2,3]
python3 中range(1,4), 结果是 range(1,4)
python 2 中的 xrange( 0, 4 ) 改为python3 中 适用于 for 循环的变量控制改为:range(0,4)
4 python2 :字符串以 8-bit 字符串存储 ;python3: 字符串以 16-bit Unicode 字符串存储
5 python2 中打开文件: file( … ) 或 open(…); python3 中 只能用 open(…)
6 python2 中有长整型 long python 3 中统一为 int
7 python2 中 / 取整数 python3 中取小数 python3 // 取整数
8 python2 中的经典类 和 新式类
经典类 class A: pass
新式类 class A(object): pass
python3 中只有新式类(也可称都叫新式类)
新式类 class A: pass
新式类 class A(object): pass
还有很多不同…待添加

四 基本数据类型

python3 中有六个标准的基本类型,分为两类
不可变数据类型:Number(数字)、String(字符串)、Tuple(元组);
可边数据类型:List(列表)、Dictionary(字典)、Set(集合)。

五 数据结构

1 元组
(1)无法删除和修改元组内的值
(2)可以遍历或使用索引的方式获取到值
(3)可用的函数 len() max() min() list(seq)
(4)元组使用小括号
(5)创建方式 遍历名 = ([element],…)
2 列表
(1) 可以遍历或使用索引的方式获取到值
(2)如果是一个空列表,无法使用利用索引添加值,原因是:其本质是修改索引对应的值,并不是添加值
(3)创建方式 变量名 = [[element],…] 或者使用列表推导式
(4)可使用 + 拼接多个列表
(4)可用的函数 len()、max()、 min()、 list(seq)、 del seq[index]
(5) 可用的方法
list.append(obj) 在列表末尾添加新的对象
list.count(obj) 统计某个元素在列表中出现的次数
list.extend(seq) 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
list.index(obj) 从列表中找出某个值第一个匹配项的索引位置
list.insert(index, obj) 将对象插入列表
list.pop([index=-1]) 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
list.remove(obj) 移除列表中某个值的第一个匹配项
list.reverse() 反向列表中元素
list.sort( key=None, reverse=False) 对原列表进行排序
list.clear() 清空列表
list.copy() 复制列表
(6)推导式:variable = [out_exp for out_exp in input_list if out_exp == 2]
3 切片
(1)切片操作基本表达式:object[start_index : end_index : step]
(2)当start_index省略,默认为起始端;当end_index省略,默认为终止端;当step省略,默认步长为1;start和end之间有和step方向一致元素 间隔,否则会切出空列表
(3)当步长为正,从左往右,当步长为负,从右往左
(4)常用的切片 [ : ] or [ : : ]
(5)通过切片获取到的新数据,保存和原数据类型相同,并拥有原数据的的方法和函数
(6)通过切片获取到的是一个原数据左闭右开的的新序列
4 字典
(1)字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中
(2)可以遍历或通过key取值;可 dictName[key] = value 修改键值对或添加键值对
(3)可用方法len(dict) 、str(dict) del dictname[key]
(4)可用函数
radiansdict.clear() 删除字典内所有元素
radiansdict.copy() 返回一个字典的浅复制
radiansdict.fromkeys() 创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值
radiansdict.get(key, default=None) 返回指定键的值,如果值不在字典中返回default值
key in dict 如果键在字典dict里返回true,否则返回false
radiansdict.items() 以列表返回可遍历的(键, 值) 元组数组
radiansdict.keys() 返回一个迭代器,可以使用 list() 来转换为列表
radiansdict.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
radiansdict.update(dict2) 把字典dict2的键/值对更新到dict里
radiansdict.values() 返回一个迭代器,可以使用 list() 来转换为列表
pop(key[,default]) 删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。
popitem() 随机返回并删除字典中的最后一对键和值。
(5)推导式:{ key: value for key in list1 for value in list2 }
5 集合
(1)集合(set)是一个无序的不重复元素序列。作用:去重
(2)可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
(3)可用方法
set.add(x) 将元素 x 添加到集合 s 中,如果元素已存在,则不进行任何操作
set.update( x ) x 参数可以是列表,元组,字典等,x 可以有多个,用逗号分开。
set.remove( x ) 将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误。
set.discard( x ) 移除集合中的元素,且如果元素不存在,不会发生错误。
set.pop() 设置随机删除集合中的一个元素。在交互模式下,pop 是删除集合的第一个元素(排序后的集合的第一个元素)。
set.clear() 清空集合 s
x in set 判断元素 x 是否在集合 s 中,存在返回 True,不存在返回 False。
set.copy() 用于拷贝一个集合。
set.difference(set) 用于返回集合的差集,即返回的集合元素包含在第一个集合中,但不包含在第二个集合(方法的参数)中。
set.difference_update(set) 移除两个集合中都存在的元素。 差集
set.isdisjoint(set) 用于判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。
set.union(set1, set2…) 返回两个集合的并集,即包含了所有集合的元素,重复的元素只会出现一次。
set.issubset(set) 用于判断集合的所有元素是否都包含在指定集合中,如果是则返回 True,否则返回 False。
(4)可用函数 len(s)
6 单向队列
(1)队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
队列是一种先进先出(FIFO)的线性表,允许插入的一端为队尾,允许删除的一端为对首。队列不允许在中间部位进行操作。
(2)创建一个简单的队列,目标如下:
A Queue() 创建一个空的队列
B enqueue(item)往队列中添加一个item元素
C dequeue() 从对首删除一个元素
D is_empty() 判断一个队列是否为空
E size() 返回队列的大小

class Queue(object):
    
    def __init__(self):
        self.__list = []
    
    def enqueue(self, item):
        """往队列中添加一个item元素"""
        self.__list.append(item)
    
    def dequeue(self):
        """从对首删除一个元素"""
        return self.__list.pop(0)
    
    def is_empty(self):
        """判断一个队列是否为空"""
        return self.__list == []
    
    def size(self):
        """返回队列的大小"""
        return len(self.__list) 
    
if __name__ == '__main__':
    q = Queue()
    q.enqueue(1)
    q.enqueue(2)
    q.enqueue(3)
    q.enqueue(4)
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())

输出

1
2
3
4

(3)使用内置模块 导入模块 from queue import Queue
(4)单向队列的方法
在这里插入图片描述
join(阻塞调用线程,直到队列中的所有任务被处理掉)
task_done(在完成一项任务之后,向任务已经完成的队列发送一个信号)
(5)一个简单的进程间消息互通的队列

from queue import Queue

if __name__ == '__main__':
    q = Queue(3)
    q.put("消息1")
    print('队列是否满了:', q.full())
    q.put("消息2")
    print('队列是否满了:', q.full())
    q.put("消息3")
    print('队列是否满了:', q.full())

    try:
        # q.put("消息4", True, 2)
        q.put_nowait("消息4")
    except:
        print("消息队列满了,现有的消息数量:", q.qsize())
    if not q.empty():
        print("--从队列中取出消息--")
        for i in range(q.qsize()):
            print(q.get_nowait())

    if not q.full():
        q.put('消息4')
        print(q.qsize())

7 双向队列
(1)双端队列 (deque ) 是一种具有队列和栈的性质的数据结构,双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行,双端队列可以在队列的任意一端入队和出队。
(2)实现一个简单的双端队列,目标如下:
A Deque() 创建一个空的双端队列
B add_front(item) 从队首添加一个item元素
C add_rear(item) 从队尾添加一个item元素
D remove_front() 从队首删除一个item元素
E remove_rear() 从队尾删除一个item元素
F is_empty() 判断双端队列是否为空
G size() 返回队列的大小

class Deque(object):
    
    def __init__(self):
        self.__list = []
     
    def add_front(self, item):
        """从队首添加一个item元素"""
        self.__list.insert(0, item)
    
    def add_rear(self, item):
        """从队尾添加一个item元素"""
        self.__list.append(item)
    
    def remove_front(self):
        """从队首删除一个item元素"""
        return self.__list.pop(0)
    
    def remove_rear(self):
        """从队尾删除一个item元素"""
        return self.__list.pop()
    
    def is_empty(self):
        """判断双端队列是否为空"""
        return self.__list == []
    
    def size(self):
        """返回队列的大小"""
        return len(self.__list)
    
if __name__ == '__main__':
    d = Deque()
    print(d.is_empty())
    d.add_front(1)
    d.add_front(2)
    d.add_front(3)
    d.add_rear(4)
    print(d.is_empty())
    print(d.size())
    print(d.remove_front())
    print(d.remove_rear())

输出

True
False
4
3
4

(3)使用内置模块 导入 from collections import deque
(4)使用方法
首先创建一个双向队列对象
d = deque
在这里插入图片描述
8 枚举
(1)⼀个有⽤的容器是枚举对象,它属于enum模块,存在于Python 3.4以上版本中。Enums(枚举类型)基本上是⼀种组织各种东 西的⽅式。
(2)模块导入 from enum import Enum, 枚举定义用class关键字,继承Enum类。
(3)简单的实例

class Species(Enum):
    cat = 1
    dog = 2
    hores = 3
    aardvark = 4
    butterfly = 5
    owl = 6
    platypus = 7
    dragon = 8
    unicorn = 9

(4)几个重要的点
A 定义枚举时,成员名称不允许重复;

from enum import Enum

class Color(Enum):
    red = 1
    red = 2

print(Color.red)

会报错

B 默认情况下,不同的成员值允许相同。但是两个相同值的成员,第二个成员的名称被视作第一个成员的别名;

from enum import Enum

class Color(Enum):
    red = 1
    blue = 1

print(Color.red.name)
print(Color.blue.name)

输出

# red
# red

C 如果枚举中存在相同值的成员,在通过值获取枚举成员时,只能获取到第一个成员;
实例同上一个
D 如果要限制定义枚举时,不能定义相同值的成员。可以使用装饰器@unique【要导入unique模块】

from enum import Enum, unique

@unique
class Color(Enum):
    red = 1
    blue = 1

会报错

(5)枚举取值
A 通过成员的名称来获取成员

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2

print(Color['red'])
print(Color['blue'])

输出

# Color.red
# Color.blue

B 通过成员值来获取成员

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2

print(Color(1))
print(Color(2))

输出

# Color.red
# Color.blue

C 通过成员,来获取它的名称和值

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2

red_member = Color.red
print(red_member.name)
print(red_member.value)

输出

# red
# 1

(6)枚举支持迭代器,可以遍历枚举成员

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2

for colorN in Color:
    print(colorN)

print("+++++++++++")
for colorV in Color:
    print(colorV.value)

输出:

# Color.red
# Color.blue
# +++++++++++
# 1
# 2

(7)如果枚举有值重复的成员,循环遍历枚举时只获取值重复成员的第一个成员.

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2
    yellow = 1
for colorN in Color:
    print(colorN)

输出

# Color.red
# Color.blue

(8)如果想把值重复的成员也遍历出来,要用枚举的一个特殊属性__members__

from enum import Enum

class Color(Enum):
    red = 1
    blue = 2
    yellow = 1
for colorN in Color.__members__.items():
    print(colorN)

输出

# ('red', <Color.red: 1>)
# ('blue', <Color.blue: 2>)
# ('yellow', <Color.red: 1>)

members 的方法有很多。
object.members.keys() 获取枚举对象中的所有key,包括值重复的,只打印 red, blue …
object.members.values()
object.members.get(“red”).value
以上列举了三个
(8)枚举的比较
A 枚举成员可进行同一性比较
B 枚举成员可进等值比较
C 枚举成员不能进行大小比较
9 defaultdict
(1)导入模块 from collections import defaultdict
(2)简单的实例演示
实例1

from collections import defaultdict 
colours = ( 
    ('Yasoob', 'Yellow'), 
    ('Ali', 'Blue'), 
    ('Arham', 'Green'), 
    ('Ali', 'Black'), 
    ('Yasoob', 'Red'), 
    ('Ahmed', 'Silver'), 
)
favourite_colours = defaultdict(list) 
for name, colour in colours: 
    favourite_colours[name].append(colour) 

print(favourite_colours)

输出

# defaultdict(<type 'list'>, 
# {'Arham': ['Green'], 
# 'Yasoob': ['Yellow', 'Red'], 
# 'Ahmed': ['Silver'], 
# 'Ali': ['Blue', 'Black'] 
# })

实例2

from collections import defaultdict
colours = (
    ('Yasoob', 'Yellow'),
    ('Ali', 'Blue'),
    ('Arham', 'Green'),
    ('Ali', 'Black'),
    ('Yasoob', 'Red'),
    ('Ahmed', 'Silver'),
)
favourite_colours = defaultdict(dict)
for name, colour in colours:
    favourite_colours[name] = (colour)

print(favourite_colours)

输出

# defaultdict(<class 'dict'>, {
# 'Yasoob': 'Red', 
# 'Ali': 'Black', 
# 'Arham': 'Green', 
# 'Ahmed': 'Silver'
# })

仔细观察 实例1和实例2的区别
colours 这个变量中的值,可以是set 或list, 但是每一个set或list 中有且只能是两个元素
重要点
对一个常规的字典进行嵌套赋值时,如果这个键不存在,会发生keyerror异常。
为了解决这个问题,使用defaultdict可以有效的解决这个问题
先来看一下 keyerror的情况

a = {}
a["b"]["c"] = "d"
print(a)

输出

# Traceback (most recent call last):
#  FileName
#    a["b"]["c"] = "d"
# KeyError: 'b'

当使用了 defaultdict后

from collections import defaultdict

tree = lambda: defaultdict(tree)
a = tree()
a['b']['c'] = 'd'
print(a)

import json

print(json.dumps(a))

输出

#第一次print打印 # defaultdict(<function <lambda> at 0x000001C8BBCEE040>, 
# {'b': defaultdict(<function <lambda> at 0x000001C8BBCEE040>, 
# {'c': 'd'})}) 
# 第二次print打印 
# {"b": {"c": "d"}}

从输出上看出defaultdict完美的解决的了在嵌套中不存在key情况下的赋值
这里用到了json模块
10 namedtuple
(1)namedtuple 具有tuple 的一些性值,但是也有一些个性,它把元组变成⼀个针对简单任务的容器,不必使⽤整 数索引来访问⼀个namedtuples的数据,可以像字典(dict)⼀样访问namedtuples, 但namedtuples是不可变的。
(2)导入模块 from collections import namedtuple
(3)简单的实例

from collections import namedtuple

Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="tom", age=12, type="cat")

print(perry)
print(perry.name)
print(perry.age)
print(perry.type)

输出

# Animal(name='tom', age=12, type='cat')
# tom
# 12
# cat

(4)⼀ 个命名元组(namedtuple)有两个必需的参数。它们是元组名称和字段名称。在上⾯的例⼦中,我们的元组名称是Animal,字段名称是’name’,‘age’和’type’。namedtuple的每个实例没有对象字典,所以它们很轻量,与普通的元组⽐,并不 需要更多的内存。这使得它们⽐字典更快。记住它是⼀个元组,属性值在namedtuple中是不可变的
(5)可以使用索引读取值

from collections import namedtuple

Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="tom", age=12, type="cat")

print(perry[0])
print(perry[1])
print(perry[2])

输出

# tom
# 12
# cat

(6)将⼀个命名元组转换为字典

from collections import namedtuple

Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="tom", age=12, type="cat")

print(perry._asdict())

输出

# {'name': 'tom', 'age': 12, 'type': 'cat'}

11 迭代器
(1)迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
上面的这句话一定要好好理解一下,很重要
(2)迭代器有两个基本的方法:iter() 和 next()。字符串,列表或元组对象都可用于创建迭代器:
(3)iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
(4)next() 方法会返回下一个迭代器对象。
(5)使用了 yield 的函数被称为生成器.
12 生成器
(1)普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。
13 栈
(1)栈(stack),有些地方称为堆栈,是一种容器,可存入数据元素、访问元素、删除元素,它的特点在于只能允许在容器的一段(称为栈顶端指标,top)进行加入数据(push)和输出数据(pop)的运算。没有了位置概念,保证任何时候访问、删除的元素都是此前最后存入的那个元素,确定了一种默认的访问顺序。由于栈数据结构只能允许在一端进行操作,因而按照后进先出(LIFO)的原理运作。
(2)图示意
在这里插入图片描述
(3)实现一个简单的栈,目标如下:
A stack() 创建一个新的空栈
B push(item)添加一个新的元素item到栈顶
C pop() 弹出一个栈顶元素
D peek() 返回栈顶元素
E is_enpty() 判断是否为空
F size() 返回栈的元素个数

class Stack(object):
    """栈"""
    def __init__(self):
        self.__list = [] # 初始化一个对隐藏的list
    
    def push(self, item):
        """添加一个新的元素item到栈顶"""
        self.__list.append(item)
    
    def pop(self): 
        """弹出一个栈顶元素"""
        return self.__list.pop()
    
    def peek(self):
        """返回栈顶元素"""
        if self.__list:
            return self.__list[-1]
        else:
            return None
    
    def is_empty(self):
        """判断栈是否为空""" 
        return self.__list == []
        
    def size(self):
        """返回栈的长度"""
        return len(self.__list)
    
if __name__ == '__main__':
    s = Stack()
    s.push(1)
    s.push(2)
    s.push(3)
    s.push(4)
    print(s.pop())
    print(s.pop())
    print(s.pop())
    print(s.pop())

输出

4
3
2
1

13 单向链表

import copy

class Node:

    def __init__(self, item):
        self.item = item
        self.next = None

class SingleLinkList:

    def __init__(self):
        """
        初始化链表
        """
        self.__head = None
        self.__tail = None

    def is_empty(self):
        """
        判断是否为空
        """
        return self.__head == None

    def length(self):
        """
        返回链表的长度
        """
        cur = self.__head
        count = 0
        while cur != None:
            count += 1
            cur = cur.next 
        return count
    
    def add(self, item):
        """
        在链表的头部插入值
        """
        node = Node(item) # 创建节点
        if self.is_empty():
            self.__head = node
            return
        node.next = self.__head # 新节点的链接域指向头节点
        self.__head = node # 将链表的头指向新节点
        
    def append(self, item):
        """
        在链表的尾部插入值
        """
        node = Node(item)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next != None:
                cur = cur.next
            cur.next = node

    def insert(self, pos, item):
        """
        在指定位置插入值
        """
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            node = Node(item)
            count = 0
            pre = self.__head 
            while count < (pos - 1):
                count += 1
                pre = pre.next
            node.next = pre.next # 将新节点的链表域指向插入位置的节点
            pre.next = node # 将前一个节点的链表域指向新节点

    def travel(self):
        """
        遍历链表
        """
        cur = self.__head
        while cur != None:
            print(cur.item, end=" ")
            cur = cur.next
        print()

    def remove(self, item):
        """
        删除节点
        """
        cur = self.__head
        pre = None
        while cur != None:
            if cur.item == item: # 找到要删除的节点
                if not pre: # 是第一个节点
                    self.__head = cur.next
                else:
                    pre.next = cur.next 
                break
            else:
                pre = cur # 游标指向下一个
                cur = cur.next # 游标指向下一个

    def search(self, item):
        """
        查找节点是否存在
        """
        cur = self.__head
        while cur != None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

    def clear(self):
        """
        清空链表
        """
        self.__head = None

    def reverse(self):
        """
        反转链表:先对原链表做头删操作,再对新链表做头插
        """
        new_single_link_list = SingleLinkList()
        temp = copy.deepcopy(self.__head)
        while temp:
            node = temp
            temp = temp.next
            if new_single_link_list.__head is None:
                new_single_link_list.__tail = node
            node.next = new_single_link_list.__head
            new_single_link_list.__head = node 

        return new_single_link_list

    def reverse2(self):
        """
        反转链表方式二:通过3个指针实现,p0指向前一个节点的指针,p1表示当前节点的指针,p2表示下一个节点的指针
        """
        p1 = copy.deepcopy(self.__head)
        p2 = p1.next
        # 定义一个新的链表对象
        new_list_node = SingleLinkList()
        while p1:
            if new_list_node.head is None:
                new_list_node.tail = p1
            # 将p1的next指向新链表的头结点
            p1.next = new_list_node.head
            # 将新链表的头结点指向p1
            new_list_node.head = p1
            # 将p1指向p2
            p1 = p2
            # 判断p2是否指向了链表的末尾
            if p2:
                p2 = p2.next
        return new_list_node

    def reverse3(self, node):
        """
        反转链表方式三:用递归实现
        """
        if node.next is None:
            return SingleLinkList(node, node)
        temp = copy.deepcopy(node)
        # 断开当前节点的连接
        temp.next = None
        # 将当前节点放到内层递归返回的链表结尾
        l = self.reverse_list(node.next)
        l.tail.next = temp
        l.tail = temp
        return l

    def getAllNode(self):
        node_list = []
        temp = self.__head
        while temp:
            node_list.append(temp.item)
            temp = temp.next
        return node_list

if __name__ == '__main__':
    s = SingleLinkList()
    # print(s.is_empty())
    s.append(1)
    s.append(2)
    s.append(3)
    print(s.length())
    # s.travel()
    # ss = s.reverse()
    # print(ss.getAllNode()

14 双向链表

class Node:

    def __init__(self, item=None):
        self.item = item
        self.next = None
        self.prev = None

class DoubleLinkList:

    def __init__(self):
        """
        初始化
        """
        self.__head = None

    def is_empty(self):
        """
        判断链表是否为空
        """
        return self.__head == None

    def length(self):
        """
        获取链表长度
        """
        cur = self.__head
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """
        遍历链表    
        """
        if self.is_empty():
            print("空链表")
            return
        cur = self.__head
        while cur != None:
            print(cur.item, end=" ")
            cur = cur.next 
        print()

    def add(self, item):
        """
        在链表头部插入节点
        """
        node = Node(item)
        if self.is_empty():
            self.__head = node
            return
        node.next = self.__head
        self.__head.prev = node
        self.__head = node
        
    def append(self, item):
        """
        在链表尾部插入节点
        """
        if self.is_empty():
            self.add(item)
            return
        cur = self.__head
        while cur.next != None:
            cur = cur.next
        node = Node(item)
        node.prev = cur
        cur.next = node

    def insert(self, pos, item):
        """
        在指定位置插入节点
        """
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            cur = self.__head
            for _ in range(pos):
                cur = cur.next
            node = Node(item)
            node.prev = cur.next
            node.prev = cur
            cur.next.prev = node
            cur.next = node

    def remove(self, item):
        """
        移除指定节点
        """
        cur = self.__head
        while cur is not None:
            if cur.item == item:
                if cur == self.__head:
                    self.__head = self.__head.next
                    if cur.next:
                        cur.next.prev = None
                    return
                if cur.next is None:
                    cur.prev.next = cur.next
                    return
                cur.prev.next = cur.next
                cur.next.prev = cur.prev
                return
            cur = cur.next

    def clear(self):
        """
        清空链表
        """
        self.__head = None
         
    def reverse(self):
        """
        反转链表
        """
        new_double_link_list = DoubleLinkList()
        cur = self.__head
        while cur != None:
            new_double_link_list.add(cur.item)
            cur = cur.next
        new_double_link_list.travel()

    def search(self,item):
        """
        查找指定节点是否存在
        """
        cur = self.__head
        while cur != None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

if __name__ == '__main__':
    ll = DoubleLinkList()
    ll.append(1)
    ll.append(2)
    ll.append(3)
    ll.travel()
    ll.reverse()

15 判断链表是否有环链

class LNode:
    def __init__(self, elem):
        self.elem = elem
        self.pnext = None
 
def exitLoop(LList):
    p1 = p2 = LList
    while p2 and p2.pnext: #当链表为空或者只有一个结点时,就不执行循环体里的程序,返回False
        p1 = p1.pnext
        p2 = p2.pnext.pnext
        if p1 == p2:
            return True
    return False
 
 
if __name__=="__main__":
    LList = LNode(1)
    p1 = LNode(2)
    p2 = LNode(3)
    p3 = LNode(4)
    p4 = LNode(5)
    p5 = LNode(6)
    LList.pnext = p1
    p1.pnext = p2
    p2.pnext = p3
    p3.pnext = p4
    p4.pnext = p5
    p5.pnext = p2
    print(exitLoop(LList))

16 哈希表
哈希表:一个通过哈希函数来计算数据存储位置的数据结构,通常支持以下操作:insert get delete
直接寻址表:key为k的元素放到k位置上
改进直接寻址表:哈希
构建大小为m的寻址表T
key为k的元素放到h(k)位置上
h(k)是一个函数,其将域U映射到表T[0,1,2,3…,m-1]
哈希冲突:由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此,对于任何哈希函数,都会出现两个不同元素映射大同一个位置上的情况,这种情况叫做哈希冲突
解决哈希冲突 —开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值
解决哈希冲突—拉链法:哈希表每一个位置都连接一个链表,当发生冲突时,冲突的元素将被加到该位置的链表最后
哈希表–常见哈希函数
m 是 list 长度
在这里插入图片描述

class linkList:
    class Node:
        def __init__(self, item):
            self.item = item
            self.next = None

    class linkListIterator:
        def __init__(self, node):
            self.node = node

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
            else:
                raise StopIteration

        def __iter__(self):
            return self

    def __init__(self, iterable=None):
        self.head = None
        self.tail = None
        if iterable:
            self.extend(iterable)

    def append(self, obj):
        s = linkList.Node(obj)
        if not self.head:
            self.head = s
            self.tail = s
        else:
            self.tail.next = s
            self.tail = s

    def extend(self, iterable):
        for obj in iterable:
            self.append(obj)

    def find(self, obj):
        for n in self:
            if n == obj:
                return True
        else:
            return False

    def __iter__(self):
        return self.linkListIterator(self.head)

    def __repr__(self):
        return "<<" + ",".join(map(str, self)) + ">>"


class HashTable:

    def __init__(self, size=101):
        self.size = size
        self.T = [linkList() for _ in range(self.size)]

    def hh(self, k):
        return k % self.size

    def insert(self, k):
        i = self.hh(k)
        if self.find(k):
            print("重复插入")
        else:
            self.T[i].append(k)


    def find(self, k):
        i = self.hh(k)
        return self.T[i].find(k)


if __name__ == '__main__':
    ht = HashTable()
    ht.insert(0)
    ht.insert(1)
    ht.insert(102)
    print(ht.T)
    print(ht.find(102))

六 文件操作

1 文件读写的基本方法
A 使用open() 方法开发文件,但是需要关闭文件
B 使用 with open 打开文件,不需要写关闭文件,自带关闭文件
2 读取json文件

    with open("./xxx.json", 'r') as fp:
        json_data = json.load(fp)
    print(json_data)

3 读取conf文件

导入模块
import configparser

conf = configparser.ConfigParser() # 获取一个读取conf文件的句柄
conf.read("myconf.conf") # 读取conf文件
sections = conf.sections() # 获取一个配置项内容 返回的是一个list
option = conf.options("logging") # 获取配置项内容中key对应的value
value = conf.get('logging', 'path') # 根据某个sections 下的某个key 获取对应的value

七 进程及队列

1 什么是进程
进程是程序的一次执行过程,是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
2 一个简单的实例

from multiprocessing import Process
import time
 
def test(delay):
    time.sleep(delay)
    print("我是子进程")
 
def main():
    print("主进程开始")
    p = Process(target=test, args=(3,))
    p.start()
    print("主进程结束")
     
if __name__ == '__main__':
    main()

输出

# 主进程开始
# 主进程结束
# 我是子进程

3 multiprocessing 模块的参数说明
在这里插入图片描述
在这里插入图片描述
4 参数的简单使用

from multiprocessing import Process
import time
import os
def child_1(delay):
    print("子进程(%s)run,它的父进程(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()
    time.sleep(delay)
    t_end = time.time()
    print("子进程(%s)执行了(%0.2f)秒" % (os.getpid(), t_end-t_start))
 
def child_2(delay):
    print("子进程(%s)run,它的父进程(%s)" % (os.getpid(), os.getppid()))
    start = time.time()
    time.sleep(delay)
    end = time.time()
    print("子进程(%s)执行了(%0.2f)秒" % (os.getpid(), end-start))
 
def main():
    print("主进程开始")
    print("主进程的PID:", os.getpid())
    p1 = Process(target=child_1, args=(1,))
    p2 = Process(target=child_2, name='mrsoft', args=(2,))
    p1.start()
    p2.start()
    print("p1.is_alive=", p1.is_alive())
    print("p2.is_alive=", p2.is_alive())
    print("p1.name:", p1.name)
    print("p1.pid:", p1.pid)
    print("p2.name:", p2.name)
    print("p2.pid:", p2.pid)
    p1.join()
    p2.join()
    print("主进程结束")
     
if __name__ == '__main__':
    main()

输出

#主进程开始
#主进程的PID: 17544
#p1.is_alive= True
#p2.is_alive= True
#p1.name: Process-1
#p1.pid: 10280
#p2.name: mrsoft
#p2.pid: 17620
#子进程(10280)run,它的父进程(17544)
#子进程(17620)run,它的父进程(17544)
#子进程(10280)执行了(1.01)秒
#子进程(17620)执行了(2.00)秒
#主进程结束

** 5 主进程和子进程的执行流程**
在这里插入图片描述
** 6 使用Process 子类创建进程**


from multiprocessing import Process
import time
import os
 
 
class SubProcess(Process):
     
    def __init__(self, interval, name=''):
        super(SubProcess, self).__init__() # 调用父类的初始化方法
        # Process.__init__(self) # 调用父类的初始化方法
        self.interval = interval
        if name :
            self.name = name
     
    def run(self):
        print("子进程(%s)run,它的父进程(%s)" % (os.getpid(), os.getppid()))
        start = time.time()
        time.sleep(self.interval)
        end = time.time()
        print("子进程(%s)执行了(%0.2f)秒" % (os.getpid(), end-start))
 
def main():
    print("主进程开始")
    print("主进程的PID:", os.getpid())
    p1 = SubProcess(interval=1, name='mrsoft')
    p2 = SubProcess(interval=2)
    p1.start()
    p2.start()
    print("p1.is_alive=", p1.is_alive())
    print("p2.is_alive=", p2.is_alive())
    print("p1.name:", p1.name)
    print("p1.pid:", p1.pid)
    print("p2.name:", p2.name)
    print("p2.pid:", p2.pid)
    p1.join()
    p2.join()
    print("主进程结束")
     
if __name__ == '__main__':
    main()

输出

# 主进程开始
# 主进程的PID: 32710
# p1.is_alive= True
# p2.is_alive= True
# p1.name: mrsoft
# p1.pid: 32711
# p2.name: SubProcess-2
# p2.pid: 32712
# 子进程(32711)run,它的父进程(32710)
# 子进程(32712)run,它的父进程(32710)
# 子进程(32711)执行了(1.00)秒
# 子进程(32712)执行了(2.00)秒
# 主进程结束

** 7 使用进程池Pool创建进程**
在这里插入图片描述

from multiprocessing import Pool
import os
import time

def task(name):
    print('子进程 (%s) 执行的任务(%s)' % (os.getpid(), name))
    time.sleep(1)
 
if __name__ == '__main__':
    print('父进程 (%s)' % os.getpid())
    p = Pool(10)
    for i in range(10):
        p.apply_async(task, args=(i,))
    
    p.close()
    p.join()
    print("所有子进程结束")

输出

# 父进程 (29089)
# 子进程 (29119) 执行的任务(0)
# 子进程 (29123) 执行的任务(1)
# 子进程 (29122) 执行的任务(2)
# 子进程 (29127) 执行的任务(3)
# 子进程 (29128) 执行的任务(4)
# 子进程 (29129) 执行的任务(5)
# 子进程 (29131) 执行的任务(6)
# 子进程 (29132) 执行的任务(7)
# 子进程 (29135) 执行的任务(8)
# 子进程 (29139) 执行的任务(9)
# 所有子进程结束

** 8 队列在进程中的使用**
由于进程之间不共享数据
在这里插入图片描述


from multiprocessing import Process, Queue
import time
 
def write_task(q):
    if not q.full():
        for i in range(5):
            msg = '消息' + str(i)
            q.put(msg)
            print("写入:", msg)
 
def read_task(q):
    time.sleep(1)
    while not q.empty():
        print("读取:%s" % q.get(True, 2))
 
if __name__ == '__main__':
    print('--主进程开始--')
    q = Queue()
    pw = Process(target=write_task, args=(q,))
    pr = Process(target=read_task, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    pr.join()
    print("--主进程结束--")

输出

--主进程开始--
写入: 消息0
写入: 消息1
写入: 消息2
写入: 消息3
写入: 消息4
读取:消息0
读取:消息1
读取:消息2
读取:消息3
读取:消息4
--主进程结束--

9 run和start的使用
在这里插入图片描述

八 线程及队列

1 什么是线程
线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。同一进程的各线程共享整个进程的资源(寄存器、堆栈、上下文)
2 一个简单的实例

import threading
import time
 
def thread():
    for i in range(3):
        time.sleep(1)
        print("thread name is %s" % threading.current_thread().name) #  使用current_thread().name 方法 获取线程名
 
if __name__ == '__main__':
    print("--主线程开始--")
    threads = [threading.Thread(target=thread) for i in range(4)]
    for t in threads:
        t.start()
         
    for t in threads:
        t.join()
    print("--主线程结束--")

输出

--主线程开始--
thread name is Thread-1
thread name is Thread-4
thread name is Thread-3
thread name is Thread-2
thread name is Thread-4
thread name is Thread-2
thread name is Thread-3
thread name is Thread-1
thread name is Thread-4
thread name is Thread-2
thread name is Thread-3
thread name is Thread-1
--主线程结束--

3 threading模块的参数说明
在这里插入图片描述
4 参数的简单使用

5 使用thread子类创建线程

from threading import Thread
import time
 
class SubThread(Thread):
     
    # def __init__(self):
    #     Thread.__init__(self)
    #     pass
     
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "子线程 " + self.name + "执行,i=" + str(i)
            print(msg)
 
if __name__ == '__main__':
    print("--主线程开始--")
    t1 = SubThread()
    t2 = SubThread()
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("--主线程结束--")

对比 进程的创建,并没有使用父类的初始化方法,原因是:没有参数传入,如果有参数传入,需要添加初始化方法。
6 队列在线程中的使用

from threading import Thread
 
 
g_num = 100
 
def puls():
    print('子线程1开始')
    global g_num
    g_num += 50
    print('g_num is %d' % g_num)
    print('--子线程1结束--')  
 
def minus():
    print('子进程2开始')
    global g_num
    g_num -= 50
    print('g_num is %d' % g_num)
    print('--子进程2结束--')
     
if __name__ == '__main__':
    print("--主进程开始--")
    t1 = Thread(target=puls)
    t2 = Thread(target=minus)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("--主进程结束--")

输出

--主进程开始--
子线程1开始
g_num is 150
--子线程1结束--
子进程2开始
g_num is 100
--子进程2结束--
--主进程结束--

可以看出资源时共享的,但是存在资源竞争,导致获取资源的不确定性,需要有一个限制,当一个线程在获取资源时,其他线程处于等待状态,让线程获取资源呈现一种排队获取资源的现象。于是需要加一把 “锁”, 称为线程安全锁或线程同步锁!
7 什么是锁
当程序运行到获取资源的时候,可能村存在资源竞争的情况,当又一个线程正在操作资源时,其他线程必须等待,等其释放后,其他线程再进行资源的竞争。
python的中的锁
(1)全局解释器锁
1 什么是全局解释器锁?
答:在同一个进程中只要有一个线程获取了全局解释器(cpu)的使用权限,那么其他的线程就必须等待该线程的全局解释器(cpu)使用权消失后才能使用全局解释器(cpu),即时多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。
2 全局解释器锁的好处?
答:1、避免了大量的加锁解锁的好处;2、使数据更加安全,解决多线程间的数据完整性和状态同步。
3 全局解释器的缺点.?
答:多核处理器退化成单核处理器,只能并发不能并行。
(2)同步锁
1 什么是同步锁?
答:同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。
2 为什么用同步锁?
答:因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程  序结果的完整性。
3 怎么使用同步锁?
只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。
(3)递归锁和死锁
1 什么是死锁?
答:指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源而相互等待的一个现象。
2 什么是递归锁?
答:在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter。变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
(4)信号量(semaphore)
1 什么是信号量?
同进程的一样,semaphore管理一个内置的计数器,每当调用acquire()时内置函数-1,每当调用release()时内置函数+1。计数器不能为0,当计数器为0时acquire()将阻塞线程,直到其他线程调用release()。

九 错误与异常

1 什么是语法错误
变量名、方法名、模块名、符号的错误,及缩进错误,等
2 什么是程序异常
错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。
3 异常的通用写法
在这里插入图片描述
try…except 必写
其他视情况而定
使用 raise 语句抛出一个指定的异常
raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

4 抛出异常
关键字 raise 允许程序员主动强制抛出一个指定的异常
要抛出的异常由 raise 的唯一参数标识。它需要一个异常实例或异常类

try:
    raise NameError("ni hao ")
except NameError as e:
    print("name error")

5 自定义异常

class MyError(Exception):

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)

try:
    raise MyError("This is error")
except MyError as e:
    print("err:", e)

通过继承一个父类Exception 重新一个自定错误。
Exception 默认的 init() 被覆盖。新的方式简单的创建 value 属性。这就替换了原来创建 args 属性的方式。
6 预定义清理行为
with 语句使得文件之类的对象可以 确保总能及时准确地进行清理。

十 函数

命名空间
一般有三种命名空间:

  1. 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
  2. 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  3. 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
    在这里插入图片描述
    Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间
    找不到抛出异常

命名空间的生命周期:
命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。
因此,我们无法从外部命名空间访问内部命名空间的对象。

作用域
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

有四种作用域:

L(Local):最内层,包含局部变量,比如一个函数/方法内部。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G(Global):当前脚本的最外层,比如当前模块的全局变量。
B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
globalnonlocal关键字

当内部作用域想修改外部作用域的变量时,就要用到globalnonlocal关键字了。
函数内部只有权限访问全局变量,无法做出修改的操作。
用法再函数中声明。

1 概述
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数的规则:

  1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
  2. 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  4. 函数内容以冒号起始,并且缩进。
  5. return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

定义一个函数的语法:

def 函数名(参数列表):
    函数体

2 函数类型
无参函数
有参函数
返回值函数

3 函数调用
有、无参数函数的调用:函数名()
有返回值函数的调用:需要有变量接收或print直接输出

4 函数参数
形参:形式上的参数,数据类型不一定,由外部传入的数据类型决定。用来接收外部传入的实参,相当于赋值操作。
实参:实际传入的参数,相当于真实的值。
*args:不定长的未命名参数,当传入函数内部时,type(args) 是一个元组,拥有元组的属性和方法
**kwargs: 不定长的命名参数,当传入函数内部时,type(kwargs)是一个dict,用又字典的属性和方法
参数的默认值:函数的参数设置的默认值后,传入的参数没有指定实参,就会读取默认的值。
存在必须参数和不定长参数时,先写必须参数,再写不定长参数,顺序不可互换。

5 匿名函数
匿名函数
python 使用 lambda 来创建匿名函数。
lambda 只是一个表达式,函数体比 def 简单很多。
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

sum = lambda x, y: x - y
print(sum(1,2))

案例

d={'a':2,'c':3,'b':1}  # 首先建一个字典d

#d.items()返回的是: dict_items([('a', 1), ('c', 3), ('b', 2)])

d_order=sorted(d.items(),key=lambda x:x[0],reverse=True) # 按字典集合中,每一个元组的第二个元素排列。
                              # x相当于字典集合中遍历出来的一个元组。
print(d_order)   # 得到: [('a', 1), ('b', 2), ('c', 3)]

reverse 的True 为降序, False为升序
[int值] 只能是 0或1 ,0 是key,1 是value

6 闭包
一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。
(1)闭包条件
  A 在一个外函数中定义了一个内函数。
B 内函数里运用了外函数的临时变量。
C 并且外函数的返回值是内函数的引用。
一般情况下,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

  # 闭包函数的实例
  # outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10
    # inner是内函数
    def inner():
        #在内函数中 用到了外函数的临时变量
        print(a+b)
    # 外函数的返回值是内函数的引用
    return inner
 
if __name__ == '__main__':
    # 在这里我们调用外函数传入参数5
    #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
    # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
    demo = outer(5)
    # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
    # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
    demo() # 15

    demo2 = outer(7)
    demo2() #17

在python中定义一个函数def demo(): 的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。我们还可以进行x = demo, y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。

返回内函数,对于闭包,在外函数outer中 最后return inner,在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中。所以接下来再进行使用demo() 的时候,相当于使用了inner函数。

一个函数,如果函数名后紧跟一对括号,说明现在就要调用这个函数,如果不跟括号,只是一个函数的名字,里面存了函数所在位置的引用。

一个函数结束的时候,会把自己的临时变量都释放给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量和内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。

闭包中内函数修改外函数局部变量
 在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:
1 global 声明全局变量
2 全局变量是可变类型数据的时候可以修改
在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:
1 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
2 在python2中,没有nonlocal这个关键字,可以把闭包变量改成可变类型数据进行修改,比如列表。

7 递归
程序调用自身的编程技巧称为递归
经典案例: 斐波那契数列

def fibo(n):
    if n <= 1:
        return n
    else:
        return (fibo(n-1) + fibo(n-2))

num = int(input())
if num <= 0:
    print("error")
else:
    print("斐波那契数列:")
    for i in range(num):
        print(fibo(i))

十一 面向对象 oop

一切事物皆对象 !
面向过程和面向对象的优缺点
面向过程:可以将复杂的问题简单化;扩展性差(更新迭代维护)
面向对象:扩展性高;编程复杂度比较高

类:类是对象的抽象,类是由对象总结而来的,总结的这个过程叫做抽象
属性:特征(变量)在前面定义
方法:函数 在后面定义
实例:对象即使由类创建的一个实例,对象是由类具体实施出来的,这个过程就做实例化
面向对象的基本实现
用class关键字定义,类名使用驼峰命名法
属性调用 对象.属性名,可以在外部修改对象的属性和添加对象的属性
如果对象的属性是某个类中的属性,无法在外部使用del 删除
如果对象的属性只属于当前对象,而不存在与某个类中,可以使用del删除

类的外部使用对象来调用方法: 对象.方法名();可以在外部修改对象的方法和添加对象的方法
删除同上。
实际上,创建对象的时候,并不会把类中的属性和方法复制一份给对象,而是在对象中引用父类的方法因此在访问对象的属性时,会先去找对象自己的属性,如果没有就去找这个类的属性和方法。
一个对象由类创建以后,是一个独立的对象,会引用父类中的属性和方法,如果在对象创建后,给对象的属性或方法,进行修改或添加,那么此时等于给这个对象创建了一个自己的属性和方法,所以在删除时,只能删除对象被修改或添加的成员。

对象操作成员
成员属性
访问:对象.成员属性名
修改:对象.成员属性名 = 新值 【此时等于给这个对象创建了一个自己的属性】
添加:对象.新成员属性 = 值 【此时时给对象自己新建了一个属性】
删除: del 对象.成员属性(只能删除这个对象自己的属性)
成员方法
访问:对象.成员方法名()
修改:对象.成员方法名 = funcName 【此时等于给这个对象创建了一个自己的方法】
添加:对象.方法名 = funcName 【此时时给对象自己新建了一个方法】
删除: del 对象.方法名(只能删除这个对象自己的方法)

类操操作成员
成员属性
访问:类名.成员属性名
修改:类名.成员属性名 = 新值 【此时等于给这个类创建了一个自己的属性】
添加:类名.新成员属性 = 值 【此时时给类自己新建了一个属性】
删除: del 类名.成员属性
成员方法
访问:类名.成员方法名()
修改:类名.成员方法名 = funcName 【此时等于给这个类创建了一个自己的方法】
添加:类名.方法名 = funcName 【此时时给类自己新建了一个方法】
删除: del 类名.方法名

成员方法中的self
1 self在方法中只是一个形参,并不是关键字,可以使用this代替,推荐使用self
2 self在方法中代表的是当前这个对象自己
3 self代表调用这个方法的对象,谁调用了这个方法,self就代表谁
4 self可以在类的内部代替对象进行各种操作

方法的分类
含有self或者可以接收对象作为参数的方法:非绑定类方法
不含self或者不能接收对象作为参数的方法:绑定类方法

非绑定类方法,可以使用对象和类去访问
绑定类方法,只能通过类去访问

魔法方法
__init__ 初始化方法
触发机制:在通过类实例化对象后,自动触发的一个方法
作用:可以在对象实例化后完成对象的初始化(属性的复制)
应用的场景:文件的打开,数据的获取做一些准备工作

__del__ 析构方法
触发机制:当前类实例化的对象被销毁时自动触发
作用:关闭一些开发的资源
注意:是对象被销毁时触发了析构方法,而不是析构方法销毁了对象

对象会在哪些情况下被销毁?
1 当程序执行完毕,内存中所有的资源都会被销毁释放
2 使用del删除时
3 对象没有被引用时,会自动销毁

面向对象的三大特性

封装
使用特殊的语法对其成员属性和成员方法进行包装,达到保护和隐藏的作用。封装是为了限制一些访问和操作,但是不能全部都限制。被封装的成员知识限制了访问的权限,并不是不让访问,通常情况下,被封装的成员主要是供内部使用。

封装的级别 公共的 受保护的(属性名或方法名前加一个下划线) 私有的(属性名或方法名前加两个下划线)
注意: 在python 中并没有真正的实现在类的外部无法访问受保护或私有的属性或方法的访问的权限,只是约定俗成的编写规范

在类外部访问受保护的属性或方法
对象._属性名(方法名)
在类外部访问私有的属性或方法
对象._类名__属性名(方法名)

对象.dict 可以获取当前对象的所有属性 以字典的方式展示 key是初始化中定义的
类名.dir() 可以获取当前类的所有成员信息

继承(python3的继承方式既不是广度优先原则,也不是深度优先原则)
被其它类继承的类,这个类称为父类 或基类或超类
继承其他类的类,这个类称为字类 或派生类

当字类继承父类后,就可以去使用父类中的成员属性和方法(除了私有成员)

子类可以有自己独立的成员,也可以没有
在不指定继承的父类时,所有类都继承object

子类继承父类后,就拥有了父类中的所有成员包括魔术方法(除私有成员)
子类继承父类后,并不会把父类的成员复制给子类,而是引用
子类继承父类后,重新定义了父类中的方法,这种情况称为父类方法的重写

在子类中可以去直接调用父类中定义的方法
1 super(子类名,self).父类方法名()
2 父类名.父类方法名(self)
在外部通过的子类的实例化对象调用父类的方法
super(子类名,实例化对象名).父类方法()
子类继承父类后,定义父类中没有的方法,这种情况称为对父类的扩展
一个父类可以被多个子类继承
子类调用父类的方法时,如果该方法有参数要求,也需要传递参数
类名.mro() : 获取mro列表,就是类的继承关系
super()
使用super去调用父级的方法时,实际上是在用super调用mro列表中的上一级中的方法
使用super去访问父级的属性时,实际上是在用super访问mro列表中的上一级中的属性
super()本身调用父级方法时,传递的self对象,就是这个方法中的self对象
issubclass() 检测一个类是否是另一个类的子类

多态
对于同一个方法,由于调用的对象不同,产生了不同形态的结果
定义一个接口规范类,其他类都继承这个类,并实现(重写)父类中的方法
由于每个对象实现父类方法的方式或者或者过程都不相同,最后的结果是不一样的形态

class Animal(object):
    def __init__(self, name):
        self.name = name
 
    def talk(self):  #抽象方法,仅由约定定义
        print(self.name,'叫')    #当子类没有重写talk方法的时候调用
 
    def animal_talk(obj):  # 多态
        obj.talk()
 
class Cat(Animal):
    def talk(self):
        print('%s: 喵喵喵!' % self.name)    #重写talk方法
 
 
class Dog(Animal):
    def talk(self):
        print('%s: 汪!汪!汪!' % self.name)
 
a = Dog('a')
Animal.animal_talk(a)    #多态调用

python 没有重载

内置成员

类名.__dict__ 获取当前类的所属成员
对象.__dict__获取当前对象的所属成员

获取类文档信息(注释内容)
类名.__doc__
对象名.__doc__

获取类名称组成字符串
类名.__name__

获取类所在的文件名称,如果是当前文件,显示__main__
类名.__module__

获取当前类的父类元组
类名.__bases__
类名.__base__ 获取父类

获取当前类的继承链
类名.__mro__

方法的分类

1 对象方法
特征:
1 在类中定义的方法,含有self参数
2 含有self的方法,只能使用对象进行调用
3 该方法会把调用的对象传递进来
4 使用类名调用对象方法也可以,但是需要传self对应的参数,(不推荐使用)
2 类方法
特征:
1 在类中定义的方法,使用装饰器@classmethod进行装饰
2 方法中有cls形参
3 不需要实例化对象,直接使用类进行调用
4 会把调用这个方法的类传递进来
5 使用对象调用,传递进来的也是类调用
3 绑定方法
特征:
1 在类中定义的方法没有任何参数
2 只能使用类调用
3 不会传递对象或参数进来
4 静态方法
特征:
1 在类中定义的方法,没有任何参数
2 使用装饰器@staticmethod进行装饰
3 可用使用对象或类进行调用
4 不会传递对象或类进来

常用函数
issubclass(类名,类名) 检测一个类是否是另一个类的子类
isinstance(对象,类) 检测一个对象是否是该类或子类的实例化结果
hasttr(对象/类,“成员名称”) 检测类或对象是否包含指定名称的成员
getattr(对象/类,“成员名称”) 获取类或对象的成员
setattr(对象/类,“成员名称”,“成员的值”) 设置类或对象的成员的值
delattr(对象/类,“成员名称”) 删除对象/类的成员属性
dir(对象) 获取当前对象所有可用访问的成员列表

魔术方法

魔术不用手动调用自动触发的

初始化方法__init__
析构方法__del__关闭或释放对象创建时打开或i创建的一些资源
__new__
触发时机:实例化对象时自动触发(在__init__之前触发)
作用:管理控制对象创建的过程
参数:一个cls接收当前类,其他参数根据初始化方法的参数进行决定
返回值:必须返回object.__new__(cls)进行对象的创建,如果没有返回值,则实例化对象的结果未None
注意事项:__new__方法的参数和__init__方法的参数要保持一致,除了第一个参数
必须返回object.__new__(cls)进行对象的创建,如果没有返回值,则实例化对象的结果未None
应用场景:单例模式的创建

__call__
触发机制:把对象当作函数直接调用时自动触发
作用:一般用于归纳类或对象的操作步骤,方便调用
参数:一个self接收当前对象 其他参数根据需求添加
返回值:可有可无

魔术方法·__len__
触发时机:当使用len函数去检测当前对象的时候自动触发
作用:可用使用len函数检测当前对象中某个数据信息
参数:一个self参数接收当前对象
返回值:必须有,并且必须是一个整型
注意事项:len要获取什么属性值,就在返回值中返回哪个属性的长度即可
__str__
触发机制:当使用str或print函数对对象进行操作时自动触发
作用:代码对象进行字符串的返回,可以自定义打印的信息
参数:一个self,接收当前对象
返回值:必须有,而必须时字符串类型的值

__repr__
触发机制:在使用repr方法对当前对象进行转换时自动触发
作用:可以设置repr函数操作对象的结果
参数:一个self,接收当前对象
返回值:必须有,而且必须时字符串类型的值
注意:正常情况下,如果没有__str__这个魔术方法,__repr__方法就会代替__str__魔术方法

__bool__
触发时机:当前使用bool函数转换当前对象时,自动触发,默认情况下,对象会转为True
作用:可以代替对象进行bool类型的转换,可以转换任意数据
参数:一个self 接收对象
返回值:必须时一个布尔类型的返回值

成员相关的魔术方法
__getattribute__
触发时机: 当访问对象成员时,自动触发,无论当前成员是否存在
作用:可以在获取对象成员时,对数据进行一些处理
参数:一个self接收对象,一个itrm接收当前访问的成员名称
返回值:可有可无,返回值就是访问的结果
注意:在当前的魔术方法中,禁止使用 对象.成员 的方式进行访问,会触发递归。如果想在当前魔术方法中访问对象的成员必须使用object.__getattribute__ 来进行访问

__getattr__
触发机制:当访问对象中不存在的成员时,自动触发
作用:防止访问不存在的成员报错,也可以为不存在的成员进行赋值操作
参数:一个self接收当前对象,一个item接收当前访问的成员名称
注意:当存在__getattribute__方法时,会执行__getattribute__方法,不要在当前的方法中再次去访问这个不存在的成员变量,会触发递归

__setattr__
触发机制:当给对象的成员进行赋值操作时会自动触发(包括修改)
作用:可以限制或管理对象成员的添加和修改操作
参数:1 self接收当前对象 2 设置的成员名 3 设置的成员值
注意:在当前的魔术方法中禁止给当前对象的成员直接进行赋值操作,会触发递归操作,如果想要给当前对象成员进行赋值 需要借助object 格式 object.setattr(self,key,value)

__delattr__
触发机制:当删除对象成员时自动触发
作用:可以限制对象成员的删除,还可以删除不存在成员时防止报错
参数:1 self接收当前对象 2 item 删除成员名称
注意:在当前魔术方法中禁止直接删除对象的成员,会触发递归操作。如果想要删除当前对象的成员,需要借助object 格式 object.__delattr__(self,item)

访问成员的顺序
1 调用__getattribute__魔术方法
2 调用数据描述符
3 调用当前对象的成员
4 调用当前类的成员
5 调用非数据描述符
6 调用父类的成员
7 调用__getattr__魔术方法

描述符

当一个类中包含了三个(__get____set____delete__)魔术方法之一或全部,那么这个类就称为描述符
作用:对一个类中的某个成员进行一个详细的管理(获取修改删除)描述符就是代理了一个类中的成员的操作,描述符属于类,只能定义为类的属性
格式:把当前的描述符赋值给一个需要代理的类中的成员属性
实例:

class PersonName():

    __name = 'abc'

    def __get__(self, instance, owner):
        return self.__name

    def __set__(self, instance, value):
        self.__name = value

    def __delete__(self, instance):
        del self.__name

class Person():
    name = PersonName()

zs = Person()
zs.name = "zhangsan"
print(zs.name)

使用装饰器实现描述符

class Student():

    __score = None

    @property
    def score(self):
        print("get")
        return self.__score

    @score.setter
    def score(self, value):
        print("set")
        self.__score = value

    @score.deleter
    def score(self):
        print("del")
        del self.__score

zs = Student()
# zs.score
# print(zs.score)
zs.score = 22
# print(zs.score)
del zs.score

设计模式
单例设计模式
在当前脚本中,同一个类只能创建出一个对象去使用

class Demo():

    __obj = None

    def __new__(cls, *args, **kwargs):
        if not cls.__obj:

            cls.__obj = object.__new__(cls)

        return cls.__obj
        

装饰器decorator

在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或给类增加属性和方法)
核心思想:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或新类)

(1)函数装饰器
被装饰的函数的不带参数

import time

def outer1(func):
    def innner():
        s_time = time.time()
        func()
        e_time = time.time()
        print(e_time - s_time)
    return innner

@outer1
def test():
    print("hello world")
    time.sleep(3)
    
    
if __name__ == '__main__':
    test()

被装饰的函数带参数

import time

def outer1(func):
    def innner(a, b):
        s_time = time.time()
        func(a, b)
        e_time = time.time()
        print(e_time - s_time)
    return innner

@outer1
def test(a, b):
    print("hello world")
    print(a + b)
    time.sleep(3)
    
    
if __name__ == '__main__':
    test(3, 4)

带参数的装饰器

import time

def decorator(c): 
    def outer1(func):
        def innner(a, b):
            s_time = time.time()
            func(a, b)
            e_time = time.time()
            print(e_time - s_time)
            if c == 2:
                print("装饰的参数被使用了")
                print("传入的参数:", c)
        return innner
    return outer1

@decorator(2)
def test(a, b):
    print("hello world")
    print(a + b)
    time.sleep(3)
    
    
if __name__ == '__main__':
    test(3, 4)

(2)类装饰器

十二 网络编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值