python

一、py基础知识点

1.在py文件前面加上

#!/user/bin/env python

2.python2执行py出现ASCII码问题

在文件开头加

# encoding=utf-8

或者

# coding=utf-8

或者(官方推荐)

# _*_coding:utf-8_*_

3.py关键字

import keyword
print(keyword.kwlist)

4.常用数据类型

  • 查看类型
# 输出<class 'bool'>
type('需要查看的类型变量或字符串或数字等等')

4.1 Numbers(数值类型)

  • int

    1)二进制:0b+二进制数(注意是数字0不是字母o)

    2)八进制:0/0o+八进制数

    3)十进制:十进制数

    4)十六进制:0x+十六进制数

  • long(python2的类型,python3可以通过int自动调整)

  • float

    1)由整数和小数组成,如155.66

    2)也可以使用科学计数法1.5566e2

  • complex(复数)

    1)a+bj

    2)a,b均为浮点数,complex(a, b)

4.2 bool(布尔类型)

  • True
  • False
  • 可以当做int类型参加数学运算,使用issubclass(bool, int)判断bool是否是int子类

4.3 string(字符串str)

1)非原始字符串(\加上\或者字母表示转义,仅仅\表示续行符)

‘string’
“string”
"""string"""
'''string'''

2)原始字符串(转义字符不生效)

r‘string’
r“string”
r"""string"""
r'''string'''

3)字符串打印与多行关系

# 多行打印(也可以作为多行注释)
str1 = '''string1
string2
string3
.
.
.
'''
# 把换行的连接起来一
str2 = 'string1'\
'string2'

# 把换行的连接起来二
str2 = ('string1'
'string2')

4)字符串拼接

# 方式一:直接+拼接
result = "aaa" + "bbb"
# 方式二: 直接放在一起
result = "aaa""bbb"
# 方式三: ”xxx%sxxx" % (a, b),利用格式化符
result = "%s%s" % ("aaa", "bbb")
# 方式四: 字符串乘法 string * int
result = string * 100
print(result)

5)字符串切片

概念:获取一个字符串的某个片段

# 获取一个字符 下标法获取 注意不能越界,i是字符串下标
# i可以为负,反向遍历获取值
string[i]

# 获取一片字符串,包头不包尾[x1, x2)
string[x1:x2]

string[起始值:结束值:步长]
# 获取所有(取默认值起始值0:结束值len(string):步长1)
string[::]
# 相当于
string[0:len(string):1]
# 反向获取整个字符串
string[::-1]
# 相当于
string[-1:-(len(string)+1):-1]

注:步长大于0,从左到右,小于0,从右到左,不能从头部跳到尾部,从尾部跳到头部,即起始值与结束值要与方向保持一致

6)字符串方法

# 字符串查找的方法
# 获取字符串长度
len(string)

# 查找方法
# 对象.find(目标片段, 起始值, 结束值) 找到返回指定索引并立即停止遍历,找不到返回-1
print(string.find('string1', 0, len(string)))
# rfind()与find()只是方向相反,但是起始值与结束值还是同向
print(string.rfind('string1', 0, len(string))))

# 使用对象.index(string1),找不到不会返回-1,而是直接抛异常
sring.index(string1)
sring.rindex(string1)

# 使用对象.count(string1)统计出现次数
string.count(string1)

# 字符串转换的方法
'''
替换字符串
replace(old, new[, count])
old 需要替换的原字符串
new 新字符串
count 替换个数,不写默认全部
返回值: 返回替换后的新字符串
注意:原来的字符串不会改变
'''
print(string.replace(str1, str2, count))

'''
字符串第一个词首字母的大写
capitalize()
返回值: 返回修改后的新字符串
注意:原来的字符串不会改变
'''
print(string.capitalize())
print(string)

'''
字符串每个单词首字母大写(只要不是字母,其他字符隔开都认为是一个单词)
title()
返回值: 返回修改后的新字符串
注意:原来的字符串不会改变
'''
print(string.title())
print(string)

'''
字符串每个字符小写
lower()
返回值: 返回修改后的新字符串
注意:原来的字符串不会改变
'''
print(string.lower())
print(string)

'''
字符串每个字符大写
upper()
返回值: 返回修改后的新字符串
注意:原来的字符串不会改变
'''
print(string.upper())
print(string)

# 字符串填充压缩
'''
根据指定的一个字符fillchar,将原有字符串填充到指定长度width
ljust(width, fillchar)
返回值: 返回填充后的字符串
注意: 原有字符串不会改变,只有原字符串长度 < 指定结果长度时才会填充
'''
print(string.ljust(10, 'l'))
print(string)

'''
根据指定的一个字符fillchar,将原有字符串向左填充到指定长度width(字符串前面)
rjust(width, fillchar)
返回值: 返回填充后的字符串
注意: 原有字符串不会改变,只有原字符串长度 < 指定结果长度时才会填充
'''
print(string.rjust(10, 'l'))
print(string)

'''
根据指定的一个字符fillchar,将原有字符串两边填充到指定长度width(字符串在中间)
center(width, fillchar)
返回值: 返回填充后的字符串
注意: 原有字符串不会改变,只有原字符串长度 < 指定结果长度时才会填充
'''
print(string.center(10, 'l'))
print(string)

'''
移除原有字符串指定字符(默认为空白字符)
仅仅移除左侧
lstrip(chars)
参数:需要移除的字符集,移除'a'|'b'|'c'...
返回值: 返回移除后的字符串
注意: 原有字符串不会改变
'''
print("|" + string.lstrip() + "|")
print("|" + string.lstrip("wo") + "|")
print("|" + string + "|")

'''
移除原有字符串指定字符(默认为空白字符)
仅仅移除左侧
rstrip(chars)
参数:需要移除的字符集,移除'a'|'b'|'c'...
返回值: 返回移除后的字符串
注意: 原有字符串不会改变,向左没有检测到字符集的字符,直接终止
'''
print("|" + string.rstrip() + "|")
print("|" + string.rstrip("wo") + "|")
print("|" + string + "|")


# 字符串的分隔拼接
'''
将一个大的字符串分隔成几个子字符串
split(sep, maxsplit)
参数一:sep 分隔符
参数二: maxsplit 分隔次数,可以省略,有多少分隔多少
返回值:返回分隔后的字符串,组成为列表(list)
注意:不会改变原字符串
'''
result = string.split("-", 4)
print(result)
print(string)

'''
根据指定的分隔符,返回(分隔符左侧内容, 分隔符, 分隔符右侧内容)
partition(sep)
参数一:sep 分隔符
返回值:
如果找到分隔符,返回(分隔符左侧内容, 分隔符, 分隔符右侧内容) tuple类型
如果没有找到分隔符,返回(原字符串, "", "") tuple类型
注意:不会改变原字符串,从左侧开始找分隔符
'''
result = string.partition("-")
print(result)
print(string)

'''
根据指定的分隔符,返回(分隔符左侧内容, 分隔符, 分隔符右侧内容)
rpartition(sep)
参数一:sep 分隔符
返回值:
如果找到分隔符,返回(分隔符左侧内容, 分隔符, 分隔符右侧内容) tuple类型
如果没有找到分隔符,返回("", "", 原字符串) tuple类型
注意:不会改变原字符串,从右侧开始找分隔符
'''
result = string.rpartition("-")
print(result)
print(string)

'''
按照换行符(\r,\n),将字符串拆成多个元素,保存到列表中
splitlines(keepends)
参数一:keepends 是否保留换行符 bool类型
返回值:
被换行符分隔的多个字符串,作为元素组成的列表
list类型
注意:不会改变原字符串
'''
result = string.splitlines(True)
print(result)
print(string)

'''
根据指定字符串,将给定的可迭代对象,进行拼接,得到拼接后的拼接字符串
join(iterable)
参数一:iterable
可迭代对象
字符串
元祖
列表
...

返回值:拼接好的新字符串
'''
items = ["sz", "18", "gg"]
result = "-".join(items)
print(result)


# 字符串函数判定操作
'''
字符串中是否所有的字符都是字母
不包含该数字,特殊符号,标点符号等等
至少有一个字符
语法:isalpha()
返回值:是否全部是字母,bool类型
'''
print(name.isalpha())

'''
字符串中是否所有的字符都是数字
不包含该字母,特殊符号,标点符号等等
至少有一个字符
语法:isdigit()
返回值:是否全部是数字,bool类型
'''
print(string.isdigit())

'''
字符串中是否所有的字符都是数字或字母
不包含该特殊符号,标点符号等等
至少有一个字符
语法:isalnum()
返回值:是否全部是字母或数字,bool类型
'''
print(string.isalnum())

'''
字符串中是否所有的字符都是空白符
包含空格,缩进,换行等不可见转义符
至少有一个字符
语法:isspace()
返回值:是否全部是空白符,bool类型
'''
print(string.isspace())

'''
判断一个字符串是否以某个前缀开头
语法:startswith(prefix, start=0, end=len(str))
参数一:prefix 需要判断的前缀字符串
参数二:start 判定的起始位置
参数三:end 判定的终止位置
返回值:是否以指定前缀开头
bool 类型
'''
print(string.startswith("21", 2, 4))

'''
判断一个字符串是否以某个后缀结尾
语法:endswith(suffix, start=0, end=len(str))
参数一:suffix 需要判断的后缀字符串
参数二:start 判定的起始位置
参数三:end 判定的终止位置
返回值:是否以指定前缀开头
bool 类型
'''
print(string.endswith(".doc"))

# in 判断一个字符串,是否被另一个字符串包含
# not in 判断一个字符串,是否不被另一个字符串包含
print("wo" in "who am wo")
print("wo" not in "who am wo")

4.4 List(列表)

1)概念:有序可变的集合

2)定义:

  • 列表变量名 = [元素1, 元素2, …],列表的嵌套,即列表的元素可以为列表,但注意不能相互嵌套

  • 列表生成式:

    range(stop) : [0, 1, 2, …, stop-1];

    range(start, stop[, step]) : [start, start+2*step, …],step步长默认为1

    注:由于防止生成的列表没有被使用,python3做了一些改变,不会立即生成列表

  • 列表推导式:从一个list推导出另一个list;映射解析:一对一变更;过滤:从多到少

    语法:[表达式 for 变量 in 列表] 或 [表达式 for 变量 in 列表 if条件],其中for遍历可以有多个,表示多层循环遍历

    # 列表推导式
    nums = [1, 2, 3, 4, 5]
    # resList = [num ** 2 for num in nums if num % 2 != 0]
    # resList = [num ** 2 for num in nums for num2 in nums]
    resList = list(num ** 2 for num in nums for num2 in nums)
    print(resList)
  • 列表常规方法操作

    # 列表添加操作
    '''
    append
    作用:往列表最后追加一个元素
    语法:list.append(object)
    参数:object为添加的元素
    返回值:None
    注意:会修改原列表
    '''
    nums = [1, 2, 3, 4]
    print(nums)
    print(nums.append(5))
    print(nums)

    '''
    insert
    作用:往列表指定索引处追加一个元素
    语法:list.insert(index, object)
    参数:index为索引(下标),object为添加的元素
    返回值:None
    注意:会修改原列表
    '''
    nums = [1, 2, 3, 4]
    print(nums)
    print(nums.insert(1, 5))
    print(nums)


    '''
    extend
    作用:往列表拓展另一个可迭代序列
    语法:list.extend(iterable)
    参数:iterable为可迭代集合
    字符串、列表、元组、等等
    返回值:None
    注意:会修改原列表,和append区别是两个集合的拼接
    '''
    nums = [1, 2, 3, 4]
    nums2 = ["a", "b", "c"]
    print(nums)
    print(nums.extend(nums2))
    print(nums)

    # 乘法运算
    nums = [1, 2, 3, 4]
    print(nums * 3)

    # 加法运算
    nums1 = [1, 2, 3, 4]
    nums2 = ["a", "b", "c"]
    # nums3 = "abf" 不能拼接字符串
    print(nums1 + nums2)


    # 列表删除操作
    '''
    del
    作用:可以删除一个指定元素或对象
    语法:del 指定元素
    参数:无
    返回值:无
    注意:可以删除整个列表、某个元素、一个变量
    '''
    nums = [1, 2, 3, 4, 5]
    # 删除某个元素
    del nums[0]
    # 删除整个列表
    del nums
    print(nums)
    # 删除一个变量
    num = 666
    del num
    print(num)

    '''
    pop
    作用:移除并返回列表中指定索引对应元素
    语法:list.pop(index = -1)
    参数:index默认是-1,即最后一个元素
    返回值:被删除元素
    注意:会直接修改原数组,注意越界
    '''
    nums = [1, 2, 3, 4, 5]
    print(nums.pop(2))
    print(nums)

    '''
    remove
    作用:移除列表中指定元素
    语法:list.remove(object)
    参数:object是待移除的元素
    返回值:None
    注意:会直接修改原数组,如果不存在,报错,存在多个,删除最左边一个,注意循环内删除列表元素的坑
    '''
    nums = [1, 2, 2, 3, 4, 5]
    print(nums.remove(2))
    print(nums)
    nums2 = [1, 2, 2, 3, 4, 5, 2]

    for num in nums2:
    print(num)
    if num == 2:
    nums2.remove(2)
    print(nums2)

    # 修改列表
    '''
    通过下标修改指定列表的指定索引处的值
    '''
    nums = [1, 2, 3, 4, 5]
    nums[2] = 6


    # 列表查询
    # 获取单个,元素通过下标(索引)查询
    nums = [1, 2, 3, 4, 5, 5, 2, 6, 6]
    print(nums[2])

    # 获取元素索引 list.index(value,start,stop)
    print(nums.index(5, 5, 6))

    # 获取指定元素个数 count()
    print(nums.count(5))

    # 切片与字符串一样
    print(nums[1:4:1])
    print(nums[-1:-4:-2])
  • 列表查询遍历操作:

    1) 根据元素进行遍历

    # for item in list:
    values = ['a', 'b', 'c', 'd']

    for v in values:
    currentIndex = 0
    print(v)
    print(values.index(v, currentIndex))
    currentIndex += 1

    2) 根据索引进行遍历

    # for index in range(len(list)):
    values = ['a', 'b', 'c', 'd', 'e']
    for index in range(len(values)):
    print(index, values[index])

    3) 根据枚举对象进行遍历(了解)

    values = ['a', 'b', 'c', 'd', 'e']
    # 1.根据列表创建枚举对象
    # 语法:enumerate(list, [start=0])
    # print(list(enumerate(values)))
    # 2.遍历枚举对象,可以直接遍历
    for index, value in enumerate(values):
    # for tupleValue in enumerate(values):
    # print(tupleValue)
    # print(tupleValue[0])
    # print(tupleValue[1])
    # index, value = tupleValue
    print(index)
    print(value)

    4) 根据迭代器进行遍历(了解)

    l = [1, 2, 3, 5, 6]
    # 创建迭代器对象
    it = iter(l)
    # 内部自动调整指针,指向下一个索引
    print(next(it))
    print(next(it))
    print(next(it))
    print(next(it))
    print(next(it))
    # 迭代完成,继续迭代会报错StopIteration
    print(next(it))
    for v in it:
    print(v)
    print("===========")
    # 不能多次迭代,只能重新创建迭代器对象
    for v in it:
    print(v)

    5) 迭代器

    • 可迭代对象:能够被迭代的对象

      判断方法:

      from collections.abc import Iterable
      isinstance(obj, Iterable)

      判断依据:能否作用于for in

    • 迭代器是可以记录遍历位置的对象,从第一个元素开始,往后只能通过next()函数进行遍历,只能往前,不能往后

      判断方法:

      from collections.abc import Iterator
      isinstance(obj, Iterator)

      判断依据:能否作用于next()函数

      注意:迭代器也是可迭代对象,也可以使用for in

    • 迭代器产生原因:

      仅仅迭代到某个元素时才会处理该元素: 在此之前,元素可以不存在,在此之后,元素可以销毁,适用于无限大集合,例如斐波那契数列
      提供了一个统一的访问集合的接口: 可以将所有可迭代对象转换成迭代器使用

      iter(Iterable)
      iter(str)
      iter(list)
      iter(tuple)
      iter(dict)
      ...

      注:迭代完成,继续迭代会报错StopIteration,使用for in,不能多次迭代,只能重新创建迭代器对象

    6) 列表其他操作

    # 判断元素是否在集合
    # in 判断元素是否在列表中(对其他集合和字符串通用)
    # not in 判断元素是否在列表中(对其他集合和字符串通用)

    # 比较
    '''
    cmp(val1,val2)
    内建函数
    如果比较字符串,列表等集合,从左往右逐个比较
    val1 > val2 1
    val1 = val2 0
    val1 < val2 -1
    python3.x不支持

    python3使用比较运算符比较
    '''
    # python3
    print([1, 2] > [1, 3])

    # 排序方式一:内建函数,返回一个列表,原来列表不变
    # s = "happynewYear"
    s = [1, 2, 5, 6, 8, 9]
    # 升序,返回一个列表
    res = sorted(s)
    print(res)
    # print(s)
    # 降序
    res1 = sorted(s, reverse=True)
    print(res1)
    s1 = [("dd1", 18), ("dd5", 16), ("dd2", 28), ("dd1", 15)]
    # 第一个值的比较排序
    print(sorted(s1))
    def getKey(x):
    return x[1]
    # 第二个值来排序
    print(sorted(s1,key=getKey, reverse=True))

    # 排序方式二:更改对象本身,返回值为None
    val = [11, 2, 45, 56]
    print(val.sort(), val)
    s = [("dd1", 18), ("dd5", 16), ("dd2", 28), ("dd1", 15)]
    print(s.sort())
    def getKey(x):
    return x[1]
    # 第二个元素降序排序
    print(s.sort(key=getKey, reverse=True), s)

    '''
    乱序
    import random
    random.shuffle(list)
    返回值 :None
    '''
    import random
    l = [1, 5, 98, 6, 2]
    res = random.shuffle(l)
    print(res, l)

    '''
    反转
    list.reverse()
    返回值 :None
    '''
    l = [1, 5, 98, 6, 2]
    res = l.reverse()
    print(res, l)
    # 切片反转,返回值反转,不会改变本身
    res1 = l[::-1]
    print(res1, l)

4.5 set(集合)

1)概念:无序的,不可随机访问的,不可重复的元素集合

  • 可变集合set:可以增删改
  • 不可变集合frozenset:不能增删改

2)可变集合定义:

  • 集合变量名 = {元素1, 元素2, …};

  • 集合变量名 = set(iterable), 即可以转换字符串、列表、元组、字典为set集合,字典时,只生成key的集合;

  • 集合的推导式:集合变量名={推导式}或者set(推导式)

    # 集合推导式
    # set5 = set(x for x in range(10) if x % 2 == 0)
    set5 = {x for x in range(10) if x % 2 == 0}
    print(set5, type(set5))
  • 集合的嵌套,即元组的元素可以为元组,但注意不能相互嵌套

3) 不可变集合定义:

  • 集合变量名 = frozenset(iterable), 即可以转换字符串、列表、元组、字典为set集合,字典时,只生成key的集合;

  • 集合的推导式:集合变量名=frozenset(推导式)

    fs = frozenset(x for x in range(10) if x % 2 == 0)
    print(fs, type(fs))

4) 注意:

  • 创建一个空集合时需要使用set()或者frozenset(),不能使用是s={},会被识别为dict字典

  • 集合中的元素必须是可哈希的值:

    如果一对象在自己的生命周期中有一哈希值(hash value),是不可变的,那么它是可哈希的(hashable)到,暂时认为是不可变类型

    比如字典、列表是可变类型,元组、字符串是不可变类型

  • 如果集合中的元素出现重复,则会被合并为一个

5) 集合常规操作方法:

​ a)可变集合:(元素为不可变类型,不能修改)

# 集合常用操作方法
# 增 返回值None
add = s.add(4)
print(s, type(s), add)

# 删
# s.remove(element) 返回值None 没有该元素,报错KeyError
remove = s.remove(4)
# remove = s.remove(5)
print(s, type(s), remove)

# s.discard(element) 返回值None 没有该元素,什么都不会发生
discard = s.discard(5)
print(s, type(s), discard)

# s.pop(element) 随机删除并返回集合中的某个元素,集合为空,报错
# p1 = s.pop()
# print(p1)

# s.clear() 返回值None,清空集合,集合仍存在
clear = s.clear()
print(s, clear)


# 查
# 无法通过索引或key进行查询
s = {1, 2, 3}
# for v in s:
# print(v)

# 1.生成一个迭代器
its = iter(s)
# 2.使用迭代器取访问(next() 或 for in)
# print(next(its))
# print(next(its))
# print(next(its))
# 指针为null
# print(next(its))

for it in its:
print(it)

print("==========")
# 迭代一次后,迭代器对象需要重新创建才可以遍历
for it in its:
print(it)

b) 不可变集合常用方法:(不能增删改)

# 不可变集合查询
fs = frozenset([1, 2, 3])
for f in fs:
print(f)
ifs = iter(fs)

# print(next(ifs))
# print(next(ifs))
# print(next(ifs))

for i in ifs:
print(i)

c) 集合之间交并差集判定操作:(可变集合与不可变集合做混合运算,返回值类型以运算符左侧类型为主)

s1 = {1, 2, 3, 5, 6, 9}
s2 = {2, 4, 3, 9, 8}
'''
交集
intersection(iterable)
字符串(只判断非数字)
元组
集合
列表
字典(只判断key)
...

逻辑与'&' 集合本身不会改变
intersection_update(iterable)
交集计算完后会赋值给原集合,所以只适用于可变集合
'''
res = s1.intersection(s2)
# set
print(res, type(res))
s3 = {1, 2, 3, 5, 6, 9}
s4 = frozenset([2, 4, 3, 9, 8])
# frozenset
res1 = s4.intersection(s3)
print(res1, type(res1))

res2 = s1.intersection_update(s2)
# res2:None s1:{9, 2, 3} s2:{2, 3, 4, 8, 9}
print(res2, type(res2), s1, s2)

# 集合本身不会改变
# res3 = s1 & s2
# print(res3, type(res3), s1, s2)

# set() 因为不会判断数字
print(s1.intersection("123"))
s5 = {"1", "2", "5"}
# 只能判定字符串
print(s5.intersection("123"))
print(s5.intersection(["1", "2", "6"]))
# 不能存在不可哈希的值["1", "5"]
print(s5.intersection(["1", "2", ["1", "5"]]))
print(s5.intersection({"1": "vv", "2": "jj", "6": "55"}))


'''
并集
union()
返回并集
逻辑或'|'
返回并集
update()
更新并集,原左侧集合更新为并集
'''
s3 = {1, 2, 3}
# s3 = frozenset([1, 2, 3])
s4 = {4, 2, 5}

# res1 = s3.union(s4)
# res1 = s3 | s4
# None, s3:{1, 2, 3, 4, 5}
res1 = s3.update(s4)
print(res1, type(res1), s3, s4)


'''
差集
difference()
返回差集
算术运算符'-'
返回并集
update()
更新并集,原左侧集合更新为并集
'''
# s3 = {1, 2, 3}
s3 = frozenset([1, 2, 3])
s4 = {4, 2, 5}

# res1 = s3.difference(s4)
res1 = s3 - s4
# None, s3:{1, 3}
# res1 = s3.difference_update(s4)
print(res1, type(res1), s3, s4)


'''
判定
两个集合不相交:isdisjoint()
一个集合包含另一个集合:issuperset()
一个集合包含于另一个集合:issubset()
'''
set1 = {1, 2, 3}
set2 = {1, 2}
# False
print(set1.isdisjoint(set2))
# True
print(set1.issuperset(set2))
# False
print(set1.issubset(set2))
print(set1.issubset(set1))

4.6 Tuple(元组)

1)概念:有序不可变的集合

2)定义:

  • 元组变量名 = (元素1, 元素2, …);
  • 元组变量名 = (元素1, )只有一个元素时,需要写逗号;
  • 元组变量名 = 元素1, 元素2, …;
  • 元组变量名 = tuple(list);
  • 元组的嵌套,即元组的元素可以为元组,但注意不能相互嵌套

3) 元组常规操作方法:

元组不能增加、删除、修改元素。

'''
元祖查询
获取单个元素,下标法 tuple[index]
获取多个元素
tuple[start:stop:step] 与字符串和列表同理
'''

'''
元组获取
tuple.count(item), 统计元组中指定元素的个数,没有返回0
tuple.index(item), 获取元组中指定元素的索引,没有报错
内建函数
len(tuple), 返回元组的元素个数
max(tuple), 返回元组的元素最大值
min(tuple), 返回元组的元素最小值
'''

'''
in 判定元素是否在元组里面
not in判定元素是否不在元组里面
'''

# 比较
'''
cmp(val1,val2)
内建函数
如果比较字符串,列表等集合,从左往右逐个比较
val1 > val2 1
val1 = val2 0
val1 < val2 -1
python3.x不支持

python3使用比较运算符比较
'''
t = ('aa', 'bb', 'cc', "dd", "ee")
t1 = ('aa', 'bb', 'cc', "dd", "ee", "ff")
print(t > t1)
print(t == t)
print(t < t1)

# 元组拼接
'''
乘法拼接:tuple * int类型
加法拼接:tuple1 + tuple2
注意不同数据类型不能使用+拼接,保错

拆包
变量名1,变量名2... = (元素1,元素2,...)
括号可以省略
变量名1,变量名2... = 元素1,元素2,...

交换变量的值,也可以加括号
变量名1,变量名2 = =变量名2, 变量名1
'''

4.7 Dictory(字典)

1)概念:无序的可变的键值对集合

2)定义:

  • 字典变量名 = {key1: value1, key2: value2, …};
  • 类调用fromkeys,字典变量名 = dict.fromkeys(S, V=None),值不写默认为None,S可以是字符串,列表元组
  • 字典对象调用fromkeys,字典变量名 = dictory.fromkeys(S, V=None),值不写默认为None,S可以是字符串,列表元组,dictory为字典实例化对象
  • 字典的嵌套,即字典的元素可以为字典,但注意不能相互嵌套
  • 字典的key不能重复,如果重复,后值覆盖前值;key必须是任意不可变类型,value可以是任意可变与不可变类型

3)字典常规操作方法:

# 字典常规操作
# 添加一个键值对 dictory[key] = value


# 删除操作
# 删除一个键值对 del dic[key],当删除的key不存在时,报错

'''
dic.pop(key[, default])
删除指定的键值对,并返回对应的值
如果key不存在,返回给的的default值,如果没有指定default值,会报错
'''
d = {"name": "admin", "age": 18}
# v = d.pop("age")
v = d.pop("age1", 666)
print(d, v)

'''
dic.popitem()
删除按升序排序后的第一个键值对,并以元组的形式返回键值对
如果字典为空,则报错
'''
d = {"name": "admin", "age": 18}
# 返回元组
dp = d.popitem()
print(d, dp)

'''
dic.clear()
删除字典内所有键值对
返回值:None
注意:字典对象本身还存在,只是清空了内容
del是删除一个键值对或整个字典
'''
clear = d.clear()
print(d, clear)


# 修改字典:只能修改值,不能修改键
'''
修改单个键值对
dic[key] = value
key不存在就是新增操作,存在就是修改操作
'''
dic = {"name": "admin", "age": 18}
dic["age"] = 20
print(dic)

'''
批量修改键值对
oldDic.update(newDic) 返回值:None
根据新的字典,批量修改旧字典的键值对值
如果旧字典没有key,就新增该key
'''
oldDic = {"name": "admin", "age": 18}
newDic = {"name": "root", "height": 180}
update = oldDic.update(newDic)
print(oldDic, newDic, update)


# 字典查询操作
'''
查询单个键值对
方式一: dic[key],key不存在报错
方式二: dic.get(key[, default]), key不存在会返回default值,如果没有给定default值,会返回None,但是原字典不会新增该键值对
方式二: dic.setdefault(key[, default]), key不存在会返回default值,如果没有给定default值,会返回None,原字典会新增该键值对
'''
dic = {"name": "admin", "age": 18}
print(dic["age"])

# dic_get = dic.get("name1")
dic_get = dic.get("name1", "root")
print(dic_get, dic)

# dic_setdefault = dic.setdefault("name1", "root")
dic_setdefault = dic.setdefault("name1")
print(dic_setdefault, dic)

'''
获取字典所有的值: dic.values()
获取字典所有的键: dic.keys()
获取字典所有的键值对: dic.items()
python2.x与python3.x之间获取键、值、键值对之间区别
1.python2.x获取到的直接是一个列表,可以通过下标进行获取指定元素
2.python3.x获取到的是Dictionary view objects改变键值对,获取到也跟着发生变化
3.python2.x提供如下方法:
viewkeys()
viewvalues()
viewitems()
作用如同.python3.x的Dictionary view objects
'''
dic = {"name": "admin", "age": 18, "height": 666}
vs = dic.values()

ks = dic.keys()

its = dic.items()

print(ks, vs, its)

dic["address"] = "shanghai"
print(dic)
print(ks, vs, its)


# 遍历方式一:遍历所有的key,根据key获取所有的值
dic = {"name": "admin", "age": 18, "height": 666}
keys = dic.keys()
for key in keys:
print(key)
print(dic[key])
dic["address"] = "shanghai"
# 遍历方式二:直接遍历所有的键值对
items = dic.items()
print(items)
for k, v in items:
print(k, v)


# 计算键值对个数
print(len(dic))

# 判定
# x in dic判定dic中的key是否存在x
# x not in dic判定dic中的key是否不存在x
# dic.has_key(key) 判定dic是否存在key,python2.x可以使用,但是过期了,使用in代替
print("name1" in dic)
print("name1" not in dic)

4.8 NoneType(空类型)

5.数据类型转换(强类型语言)

#转换为整数
int(x)
#转换为浮点数
float(x)
#转换为表达式字符串
repr(x)
#转换为字符
chr(x)
#转换为Unicode字符
unichr(x)

# 进制转换
#转换为对应整数值十进制
ord(x)
#转换为十六进制字符串
hex(x)
#转换为八进制字符串
oct(x)
#转换为二进制字符串
bin(x)

#计算字符串的有效表达式,返回对象
eval(x)
#转换为字符串
str(x)
#将序列转换为元祖
tuple(S)
#将序列转换为集合
list(S)

6.py运算符

  • 算术运算符(不同类型运算,自动精度提升,即类型上转换)
+  -  *  /(不像c++、java,除数为非整数,有小数部分)
**(幂运算符) //(整除,只取整数部分)
%(求模) =(赋值)

# 上面运算符加上=为复合运算符
a, b, c = 1, 2, 3 #批量赋值
  • 比较运算符
>  <  !=  <>(python2不等于)  >=  <= == is(比对唯一标识) x1 <= x <= x3(链式运算符)
  • 逻辑运算符
not and or # 非零非空为真,返回结果不一定是bool

7.py输入输出

7.1 输入

  • python2
res = raw_input('请输入:') # 将内容当做字符串
res = input('提示信息') # 将内容作为代码执行 input = raw_input() + eval()
content = raw_input("qingshuru")
res = eval(content)
  • python3
res = input('提示信息') # 等同于python2的raw_input('请输入:')
# 与python2的input同理
content = input('hh:')
res = eval(content)

7.2 输出

  • python2
print value1
print value1, value2...
# 格式化输出
print '字符串 格式化输出时的数据类型和格式占位符'%(与前面占位符对应类型)
# 也可以使用索引
print '{0} {1} ...'.format(value1, value2...)
# 输出到文件
f = open('text.txt', 'w')
print >> f, 'hhhh'
# 输出不换行
print value1,
print value2,
print value3,
print ...
# 输出带分割符
print '-'.join(["1", "2", "3"])
  • python3
print(value1)
print(value1, value2...)
# 格式化输出
print('字符串 格式化输出时的数据类型和格式占位符'%(与前面占位符对应类型))
print('{0} {1} ...'.format(value1, value1...))
print(f'{value1} {value1} ...')
# 输出到文件
f = open('text.txt', 'w')
print('hhh', file=f)
# 输出不自动换行
print(values, end="")
# 输出带分隔符
print('a', 'b', 'c', sep="-")
  • 格式控制符

    %[(name)][flags][width][.precision]typecode  # []表示可选 

    1) (name) : 选择指定的名称对应的值

    englishSc = 100
    mathSC = 60
    print('数学成绩 :%(ms)d, 英语成绩:%(es)d' % ({'es': englishSc, 'ms': mathSC}))

    2) flags :

    • 空:表示右对齐
    • -:-表示左对齐
    • 空格:‘ ’表示一个空格,在正数左侧填充一个空格,与负数对齐
    • 0:表示用0填充

    3) width:表示显示宽度

    4) .precision: 表示小数点后精度

    5) typecode

    • 数值

      | 格式符 | 含义 |
      | :——: | :—————————————————————————————: |
      | i/d | 将整数、浮点数转化成十进制 |
      | o | 将整数转化成八进制 |
      | x | 将整数转化成十六进制 |
      | e | 将整数、浮点数转换成科学计数法 |
      | E | 将整数、浮点数转换成科学计数法 |
      | f | 将整数、浮点数转换成科学计数法(默认保留小数点后6位) |
      | F | 将整数、浮点数转换成科学计数法(默认保留小数点后6位) |
      | g | 自动调整将整数、浮点数转换成浮点型或科学计数法(超过6位数用科学计数法) |
      | G | 自动调整将整数、浮点数转换成浮点型或科学计数法(超过6位数用科学计数法) |

    • 字符串

      | 格式符 | 含义 |
      | :——: | :—————————————————————————————: |
      | s | 获取传入对象的_str_方法的返回值 |
      | r | 获取传入对象的_repr_方法的返回值 |
      | c | 整数:将数字转换成unicode对应的值 字符:将字符添加到指定为位置 |

注:没有将整数转为二进制,即%b; %%表示打印%

8.py分支

8.1 if分支

# 单分支
if 条件:
代码块
# 双分支
if 条件:
代码块
else:
代码块

#if-else可以嵌套
if 条件:
if 条件:
代码块
else:
代码块
.
.
.
else:
if 条件:
代码块
else:
代码块
.
.
.
# 上面代码可阅读性差,可以使用多分支
if 条件:
代码块
elif 条件:
代码块
elif 条件:
代码块
...

注:python没有switch语句

9.循环

  • while

    while 条件体:
    条件满足的代码块
    # 没有dowhile,而且有break语句else就不会执行
    else:
    条件不满足代码块
  • for

    # 与java增强for循环相同
    # 变量不用先前声明
    for 变量名 in 列表、字符串等:
    条件满足循环体
    # 没有dowhile,而且有break语句else就不会执行
    else:
    条件不满足循环体
  • break: 打断本次循环,退出循环

  • continue : 结束本次循环,继续进入下一次循环
  • pass: 占位语句,空语句,保持程序结构完整性

注:rang(a, b)函数 —》[1,b-1]

10.时间日历

import time

# 获取时间戳
res = time.time()
years = res / (365 * 24 * 60 * 60) + 1970
print(res)
print(years)

# 获取时间元组,可以不传人参数,默认当前时间元组
res = time.localtime(1564727544.8498905)
print(res)


# 时间戳获取格式化时间
t = time.time()
# 格式化时间
res = time.ctime(t)
print(res)
# 默认当前时间戳
res3 = time.ctime()
print(res3)

# 时间元组获取格式化时间
time_tuple = time.localtime()
# 格式化时间
res1 = time.asctime(time_tuple)
print(res1)
# 默认当前时间元组
res2 = time.asctime()
print(res2)

# 时间元组--》格式化日期
# 2022-10-03 09:48:37
# format_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# 22-10-03 09:49:13
format_time = time.strftime("%y-%m-%d %H:%M:%S", time.localtime())
print(format_time)

# 格式化时间--》时间元组 time.strptime(日期字符串, 格式符字符串)
# time.struct_time(tm_year=2022, tm_mon=10, tm_mday=3, tm_hour=10, tm_min=3, tm_sec=41, tm_wday=0, tm_yday=276, tm_isdst=-1)
pt = time.strptime("2022-10-03 10:03:41", "%Y-%m-%d %H:%M:%S")
print(pt)

# 通过时间元组时间戳
time_mktime = time.mktime(pt)
print(time_mktime)

# 获取当前cpu时间
# python3.8后不支持time.clock(),使用time.perf_counter()代替
# startClock = time.clock()
startClock = time.perf_counter()
for i in range(0, 1000):
print(i)
# endClock = time.clock()
endClock = time.perf_counter()
print(endClock-startClock)

# 休眠时间,让线程暂停secs秒 time.sleep(secs)
while True:
format_time = time.strftime(" %Y-%m-%d %H:%M:%S ", time.localtime())
print(format_time)
time.sleep(1)


import calendar
# 时间日历
print(calendar.month(2022, 10))


import datetime

# 获取当天日期
datetime_now = datetime.datetime.now()
print(datetime_now, type(datetime_now))
print(datetime.datetime.today())
# 单独获取年月日时分秒
print(datetime_now.year)
print(datetime_now.month)
print(datetime_now.day)
print(datetime_now.hour)
print(datetime_now.minute)
print(datetime_now.second)

t = datetime.datetime.today()
# 计算n天后的日期
# res = t + datetime.timedelta(days=7)
res = t + datetime.timedelta(days=-7)
print(t, res)

# 时间间隔
first = datetime.datetime(2020, 12, 26, 10, 30, 10)
second = datetime.datetime(2020, 12, 27, 10, 30, 10)
delta = second - first
# datetime.timedelta 时间间隔类型
print(delta, type(delta))
# 对应的秒数
print(delta.total_seconds())
  • 日期时间格式符
符号 说明
%a 当前区域设置下星期简写,如星期二Tue。
%A 当前区域设置下星期全名,如星期二Tuesday。
%b 当前区域设置下月份简写,如九月Sep。
%B 当前区域设置下月份全名,如九月September。
%c 当前区域设置下的日期和时间表示。
%C 世纪,如2019年,21世纪,则返回20。
%d 十进制数字表示的月份的某一天,范围[01,31]。
%D 日期,等效于“%m/%d/%y”(美国格式),如“09/03/19”
%e 十进制数字表示的月份的某一天,范围[1,31]。如果小时10,则数字前用一个填充一个空格。
%F ISO 8601格式的完整日期,等效于“%Y-%m-%d”,如“2019-09-03”。
%g ISO周数对应的不包含世纪的年份,等效于“%y”,除非ISO周数属于前一年或后一年,则使用前一年或后一年,范围[00,99]。
%G ISO周数对应的年份,等效于“%Y”,除非ISO周数属于前一年或后一年,则使用前一年或后一年,范围[0000,9999]。
%h 等效于“%b”。
%j 十进制表示的在一天中的天数,范围[001,366]。
%m 十进制表示的月份,范围[01,12]。
%u 十进制表示的星期,范围[1-7]。周一为一周的第一天。周一为1,依次递增。
%U 十进制表示的一年中的周数,[00,53]。星期日为一周的第一天,新年第一个星期日之前的所有日子都视为第0周。
%V 十进制表示的一年中的ISO周数,[01,53]。星期一为一周的第一天,如果包含1月1日的一周在新的一年里有四天或四天以上,则认为这周是第一周,否则就是前一年的第53周,下一周是新年的第一周。
%w 十进制表示的星期,范围[0-6]。周日为一周的第一天。周日为0,依次递增。
%W 十进制表示的一年中的周数,[00,53]。星期一为一周的第一天,新年第一个星期一之前的所有日子都视为第0周。
%x 按当前区域设置下的日期格式,如“09/03/19”。
%y 年份的后两位,范围[00,99]。
%Y 年份,范围[0000,9999]。
%H 十进制数字表示的小时(24小时制),范围[00,23]。
%I(大写i) 十进制数字表示的小时(12小时制),范围[01,12]。
%M 分钟,范围[00,59]。
%p 本地区域设置下等价于“AM”或“PM”,在许多地区是空字符串。中午视为“PM”,午夜视为“AM”。
%r 本地区域设置下12小时制时间,如02:15:11 PM。
%R 24小时制的时和分,等效于“%H%M”,如“14:16”。
%S 十进制数字表示的秒,范围[00,61]。60在表示闰秒的时间戳中有效,61是出于历史原因而支持的。
%T 24小时制的时分秒,等效于“%H:%M:%S”。
%X 按当前区域设置下的日期格式,如“02:20:24 PM”。
%z 表示与UTC/GMT的正或负时差的时区偏移量,格式为+HHMM或-HHMM,其中H表示小时数,M表示分钟数,范围是[-23:59,+23:59]。
%Z 时区名。没有时区则返回空字符。

11.函数

  • 调用:先定义后调用,类似c语言,不同于java(调用的代码可以在方法前)

  • 作用:方便代码重用;分解任务,简化程序逻辑;使代码更加模块化

  • 定义:

    '''
    函数定义方式一:
    def 函数名(形式参数名列表):
    代码块

    注意,参数列表不是数据类型的列表
    函数调用:
    方式一:
    函数名(实际参数列表)
    实际参数列表的参数与形式参数的列表一一对应
    此时的参数列表可以写一个类似元组去掉括号,例如 函数名(参数1, 参数2, 参数3, 参数4, 参数5,...)

    方式二:
    函数名(实际参数名=参数的值,...) 也可叫关键字参数:键值对
    实际参数列表的参数与形式参数的列表不需要位置上一一对应,只需要参数名对应即可,例如mysum(num2=5, num1=2)

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    函数定义方式二: *args代表的接收一个元组
    def 函数名(*形式参数名)
    代码块

    函数调用:
    函数名(实际参数列表)
    实际参数列表的参数与形式参数的列表一一对应
    此时的参数列表可以写一个元组,此时元组括号必须省略 ,例如 函数名(参数1, 参数2, 参数3, 参数4, 参数5)

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    函数定义方式三: **args代表的接收一个字典
    def 函数名(**形式参数名)
    代码块

    函数调用:
    方式一:
    函数名(实际参数列表)
    实际参数列表的参数与形式参数的列表一一对应
    此时的参数列表是类似字典,此时字典{}必须省略,冒号用等号代替,参数名称没有引号,例如 函数名(参数名称1=参数1, 参数名称2=参数2, 参数名称3=参数3, ...)
    '''
  • 参数拆包:

    def mySum(a, b, c, d):
    print(a + b + c + d)


    def myFun(*args):
    print(args)
    # 拆包
    print(*args)
    mySum(*args)
    mySum(args[0], args[1], args[2], args[3])
    # 此时只有a能接收值,且为元组,报错
    mySum(args)


    myFun(1, 2, 3, 4)

    def mySum(a, b):
    print(a + b)


    def test(**kwargs):
    print(kwargs, type(kwargs))
    # 拆包操作
    # 无法打印,报错
    # print(**kwargs)
    # 同理一个字典传参,b没有接收值报错
    # mySum(kwargs)
    mySum(**kwargs)

    #必须传a,b不然不错,即传递对应的名称才能正确的拆包传参
    test(a=15, b=12)

    '''
    参数装包:把传递进来的参数包装成一个集合
    参数拆包:把集合参数再次分解成单独的个体
    '''
  • 缺省参数:

    1) 场景:使用一个函数,使用某个数据的一个固定值或者主功能之外的小功能实现

    2) 定义:def 函数名(变量名1=默认值1, 变量名2=默认值2, …):

    ​ 函数体中即使没有传参,也可以使用,但是使用的是默认值

    3) 使用:函数名(变量1, 变量2, …) 缺省参数可以不写,为默认值

  • 参数传递:

    1)值传递:只传递值的副本,不传递地址(引用),修改值的副本对原参数值没有影响

    2)引用传递(地址传递):通过传递过来的地址可以操作原参数

    注意:python只有引用传递,如果数据类型是可变类型,可以改变原件,如果数据类型是不可变类型,不能改变原件!

  • 函数返回值:

    1) 场景:使用一个函数处理数据,需要获取数据的处理结果

    2) 定义:

    def 函数名(参数列表):
    函数体
    return 返回值


    只能返回一次,因为return后函数执行完毕,不会向下执行
    如果返回多个数据,包装成一个集合返回,可以返回列表、字典、元祖等等
  • 函数的使用描述:

    1) 场景:编写三方函数,方便别人使用,添加函数的功能和使用方式等描述在函数体最上面

    2) 定义:使用双引号引起来注释

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

    帮助信息
    """
    函数体

    3)一般函数的使用描述

    • 函数的功能
    • 参数的含义、类型、是否可以省略、默认值
    • 返回值含义、类型

    4) 查看函数帮助文档

    help(函数名)
    # 注意函数名后面没有括号

11.1 偏函数(有点想java的重载)

1) 场景:编写较多参数的函数时,如果某些参数是一个固定值,为了简化使用,可以创建一个新函数,指定使用的函数的某个参数为某个固定值,这个新函数就是偏函数

2) 语法:

  • 方式一:自己写一个新的

    def mySum(a, b, c, d=1):
    print(a + b + c + d)


    # 偏函数
    def mySum1(a, b, c=1, d=2):
    mySum(a, b, c, d)

    mySum1(1, 2)
  • 方式二:借助functools模块的partial函数

    import functools
    def mySum(a, b, c, d=1):
    print(a + b + c + d)


    # 偏函数
    newFunc = functools.partial(mySum, c=5)
    print(newFunc, type(newFunc))
    # 利用functools.partial(mySum, c=5)生成的偏函数不能多写参数,会报错
    # newFunc(1, 2, 3, 4)
    newFunc(1, 2)

    # 场景
    numStr = "1000010"
    # 二进制转换成十进制
    print(int(numStr, base=2))

    # 使用偏函数,不用重复写base参数
    int2 = functools.partial(int, base=2)
    print(int2(numStr))

11.2 高阶函数

1) 概念:当一个函数A的参数,接收的又是另一个函数时,则把这个函数A称为是高阶函数。例如sorted函数

2) 语法:

# 高阶函数
l = [{"name": "ssd1", "age": 15}, {"name": "ssd4", "age": 125}, {"name": "ssd2", "age": 25},
{"name": "ssd3", "age": 18}, {"name": "ssd5", "age": 14}]


# def getKey(dic):
# return dic["name"]
#
#
# l1 = sorted(l, key=getKey)
# print(l1)

# 自定义高阶函数
def calculate(num1, num2, calculateFunc):
return calculateFunc(num1, num2)


def addFunc(num1, num2):
return num1 + num2


def subFunc(num1, num2):
return num1 - num2


print(calculate(3, 2, addFunc))
print(calculate(3, 2, subFunc))

11.3 返回函数

1) 概念:一个函数的内部,它的返回值数据是另一个函数,把这样的操作称为返回函数

2) 语法:

# 返回函数
def getFunc(flag):
def getAdd(num1, num2, num3):
return num1 + num2 + num3

def getSub(num1, num2, num3):
return num1 - num2 - num3

# 根据flag值判断返回哪一个函数
if flag == "+":
return getAdd
elif flag == "-":
return getSub


func = getFunc("+")
print(func, type(func))
print(func(1, 3, 5))

11.4 匿名函数(lambda函数)

1) 概念:没有名字的函数。

2) 语法:lambda 参数1, 参数2, …: 表达式

3) 限制:只能写一个表达式,不能直接return;表达式的结果就是返回值;只适用于简单的操作处理。

# 匿名函数
res = (lambda x, y: x + y)(1, 2)
print(res)

func = lambda x, y: x + y
print(func(1, 2))

l = [{"name": "ssd1", "age": 15}, {"name": "ssd4", "age": 125}, {"name": "ssd2", "age": 25},
{"name": "ssd3", "age": 18}, {"name": "ssd5", "age": 14}]

# def getKey(dic):
# return dic["name"]

l1 = sorted(l, key=lambda dic: dic["age"])
print(l1)

11.5 闭包(Closure)

1) 概念:在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数又把内层函数当作返回值返回,这个内层函数+所引用的外层变量,称为闭包

2) 语法:

# 闭包函数
def func1():
a = 100
def func2():
print(a)
return func2

# 得到func2
newFunc = func1()
# 调用内部函数
newFunc()

3) 应用场景:外层函数,根据不同的参数生成不同功能的内层函数

# 案例:生成分割线
def line_config(content, length):
def line():
print("-" * (length // 2) + content + "-" * (length // 2))

return line


f = line_config("闭包函数", 100)
f()

4) 注意事项

  • 闭包中如果想要修改引用的外层变量,需要使用nonlocal关键字修饰变量,不能同时赋值,先修饰然后赋值,没有加关键字,就是闭包内新定义的变量

    # external and internal
    def external_func():
    num = 10

    def internal_func():
    # nonlocal关键字 非局部的
    nonlocal num
    num = 666
    print(num)

    # 没有加nonlocal 10
    print(num)
    internal_func()
    # 没有加nonlocal 10
    print(num)
    return internal_func


    func = external_func()
    func()
  • 闭包内引用了一个后期会发生变化的变量是需要注意

    # 闭包注意事项二
    def external_func():
    num = 10

    def internal_func():
    print(num)

    num = 100

    return internal_func


    func = external_func()
    # 函数被调用时才确定内部变量标识对应的值
    # 100
    func()


    def external_func1():
    funcs = []
    for i in range(1, 4):
    def internal_func1():
    print(i)

    funcs.append(internal_func1)

    return funcs


    func_ = external_func1()
    print(func_, type(func_))
    # 全是3
    func_[0]()
    func_[1]()
    func_[2]()


    def external_func2():
    funcs = []
    for i in range(1, 4):
    def internal_func2(num):
    # 增加内层函数,保持num = i = 1、2、3
    def inner():
    print(num)

    return inner

    funcs.append(internal_func2(i))

    return funcs


    func2 = external_func2()
    print(func2, type(func2))
    # 1、2、3
    func2[0]()
    func2[1]()
    func2[2]()

11.6 装饰器(设计模式)

1) 作用:在函数名以及函数体不改变的前提下,给一个函数附加一些额外代码

2) 案例:

  • 优化前

    # 装饰器
    # 开放封闭原则:已经写好的代码,尽可能不要修改,如果想新增功能,在原来代码基础上,单独进行扩展
    # 单一职责原则:单一功能

    def publish_novels():
    # print("login_check")
    login_check()
    print("发说说")


    def publish_pictures():
    # print("login_check")
    login_check()
    print("发图片")


    def login_check():
    print("login_check")


    # 业务逻辑代码
    btn = 2
    if btn == 1:
    # print("login_check")
    publish_novels()
    else:
    # print("login_check")
    publish_pictures()

    # 方式一:在业务逻辑代码添加登录验证代码,代码冗余度大,维护性能差
    # 方式二:在功能函数添加登录验证代码,减少代码重复度,复用性好
  • 使用装饰器优化后

    def login_check(func):
    def inner():
    print("login_check")
    func()

    return inner

    # 使用@login_check立马执行login_check方法
    @login_check
    def publish_novels():
    # print("login_check")
    # login_check()
    print("发说说")

    # publish_novels = login_check(publish_novels)相当于
    # publish_novels = def inner():
    # print("login_check")
    # func()


    # 利用python语法题@函数名代替下式
    # publish_novels = login_check(publish_novels)


    # print(publish_novels)

    @login_check
    def publish_pictures():
    # print("login_check")
    # login_check()
    print("发图片")


    # 利用python语法题@函数名代替下式
    # publish_pictures = login_check(publish_pictures)
    # print(publish_pictures)


    # 业务逻辑代码
    btn = 1
    if btn == 1:
    # print("login_check")
    publish_novels()
    # login_check(publish_novels)
    else:
    # print("login_check")
    publish_pictures()
    # login_check(publish_pictures)
  • 装饰器的叠加使用(使用多个注解)

    # decorator 装饰器叠加使用,加@decorator方法的注解,从上往下执行
    def decorator_line(func):
    def inner():
    print("-" * 100)
    func()

    return inner


    def decorator_star(func):
    def inner():
    print("*" * 100)
    func()

    return inner


    @decorator_line
    # content = decorator_line(content)
    @decorator_star
    # content = decorator_star(content)
    def content():
    print("社会我小谢,人狠话不多")


    content()
  • 对有参函数进行装饰

    # 对有参函数进行装饰
    def decorator(func):
    def inner(*args, **kwargs):
    print("*" * 50)
    print(args, kwargs)
    # 拆包
    func(*args, **kwargs)

    return inner


    @decorator
    def print_number(num1, num2, num3):
    print(num1, num2, num3)


    @decorator
    def print_number2(num1):
    print(num1)


    print_number(123, 666, num3=666)
    print_number2(123)
  • 对有返回值的函数进行装饰(无论什么场景下,保证函数返回值一致)

    # 对有返回值的函数进行装饰
    def decorator(func):
    def inner(*args, **kwargs):
    print("*" * 50)
    print(args, kwargs)
    # 拆包
    res = func(*args, **kwargs)
    return res

    return inner


    @decorator
    def print_number(num1, num2, num3):
    print(num1, num2, num3)
    return num1 + num2 + num3


    @decorator
    def print_number2(num1):
    print(num1)


    # inner函数必须要有返回值,且与被装饰者返回值一致
    res1 = print_number(123, 666, num3=666)
    res2 = print_number2(123)
    print(res1, res2)
  • 通过装饰器的注解加参数获取不同的装饰器

    # 获取装饰器,通过char控制获取不同装饰器
    def getDecorator(char):
    def decorator(func):
    def inner():
    print(char * 50)
    func()

    return inner

    return decorator


    @getDecorator("*")
    def fp():
    print("中华人民共和国万岁")


    fp()

11.7 生成器

1) 概念:是一个特殊的迭代器(迭代器的抽象层次更高)

​ 拥有迭代器特性:惰性计算数据,节省内存;能够记录状态,通过next()函数访问下一个状态;具备可迭代特性;

​ 也可以自己实现迭代器

2) 创建方式一:生成器变量名 = (推导式),即列表推导式的[]改为()

3) 创建方式二:生成器函数,函数中包含yield语句,函数执行结果就是生成器

# yield,可以阻断当前函数执行,当使用next(generator)或者generator.__next__()函数,让函数继续执行,当执行下一个yield时,又阻断
def test():
print("test函数执行")
# 遇到yield,阻断执行
yield 1
print("a")

yield 2
print("b")

yield 3
print("c")

yield 4
print("d")

yield 5
print("e")


def test():
for i in range(1, 9):
yield i
# print("a" + str(i))

g = test()
print(g)

print(next(g))
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
# print(g.__next__())

4) send()方法:

send方法有一个参数,指定的是上一次被挂起的yield语句的返回值
相比于.__next__(),可以额外的给yield语句传值
注意第一次调用t.send(None)
def test():
print("test函数执行")
res1 = yield 1 # "000"
print(res1)

res2 = yield 2
print(res2)


g = test()
# 开启调用函数
print(g.__next__())
# print(g.__next__())
# g.__next__(),默认是None,使用g.send("000")可以给上一次挂起的yield传值
print(g.send("000"))
print(g.send(None))

5) 关闭生成器

g.close()

# 遇到return语句,直接终止函数 StopIteration
return 返回值

# 与迭代器一样,生成器只能迭代一次

11.8 递归函数

1) 概念:函数A内部继续调用A

2)案例:

# 递归求n!
def recursion(n):
if n == 1:
return 1
else:
return n * recursion(n - 1)


print(recursion(9))

11.9 按位置传参数函数

概念:只能位置传参,不能键值对传参,最后一个是正斜杠/,该参数不用传

# 保证按位置传参且只能传一个
def new_method(self, /):
print("我是新来的,给我一个实例")

new_method("kk")

11.10 函数作用域

1) 变量作用域

  • 变量的作用范围(可操作范围):python是静态作用域,变量的作用域源于它在代码中的位置,在不同的位置,可能有不同的命名空间。

2) 命名空间

  • 是作用域的体现形式
  • 不同的具体操作范围

3) python-LEGB

  • L-Local:函数内的命名空间;作用范围:当前整个函数体范围
  • E-Enclosing function locals:外部嵌套函数的命名空间;作用范围:闭包函数
  • G-Global:全局命名空间;作用范围:当前模块(文件)
  • B-Builtin内建模块命名空间;作用范围:所有模块(文件)
  • 注意:python没有块级作用域,比如while、for、if后面的代码块只要执行了,代码块外面可以访问,与javascript类似
  • LEGB规则:按照L—>E–->G–>B 的顺序进行查找
  1. 基于命名空间的常见变量类型
  • 局部变量:在一个函数内部定义的变量;作用域为函数内部;查看局部变量locals()
  • nonlocal关键字仅仅适用于闭包里面函数与外层函数的变量相同的变量操作,单个函数体内使用global关键字,即可修改全局变量的值
  • 全局变量:在函数外部,文件最外层定义的变量,作用域为整个文件内部;查看全局变量globals()
  • 注意点 :
    • 结构规范:访问原则从内到外
    • 全局变量与局部变量重名:获取,就近原则;修改,加global关键字修饰变量再修改
    • 命名:全局变量一般g_变量名命名

12.python文件I/O操作

  1. 文件概念:数据存放的容器

  2. 文件的作用:持久性的存储数据内容

  3. 文件组成:

    • 文件名:同级目录下,不允许同名文件存在
    • 扩展名:.jpg、.avi、.doc、.java等等
    • 注意:一般不同的扩展名,对应不同的文件格式;不同的文件格式,有着不同的存储约定,方便程序处理
  4. 文本内容

    • 文本文件:.doc、.xls、.txt等等
    • 二进制文件:图片、视频、音乐等等
  5. 文件使用流程

    • 打开:open(“文件”, “模式”),文件指定文件名称,模式控制操作模式,返回文件对象

      模式:

      | 模式 | 含义 |
      | :—-: | :—————————————————————————————- |
      | r | 以只读方式打开文件(默认文件打开模式),文件的指针放在文件的开头,注意:文件不存在报错 |
      | w | 以只写方式打开文件,文件的指针放在文件的开头,所以写入新的内容会覆盖原文件的所有内容,注意:文件不存在会自动创建文件 |
      | a | 以追加方式(只写)打开文件,文件的指针放在文件的结尾,所以写入新的内容会新增原文件的结尾,注意:文件不存在会自动创建文件 |
      | 增加b | rb、wb、ab,如果是二进制文件,则选择此项;如图片、视频、音频等等(b为binary) |
      | 增加+ | r+、w+、a+、wb+、rb+、ab+,都是以读写模式打开,其他特性基本和+前面的模式一致,但部分操作,有细节区别 |
      | r+ | 可读可写打开文件,没有文件 No such file or directory报错;可以读操作,指向文件头,读取全部内容;可以写入,指针指向文件头部,覆盖写入的内容长度,不会全部覆盖;读写同时进行,先读后写,指针先移到读完后的位置,再写入;读写同时进行,先写后读(会等读语句执行后写入),指针移到写完后的位置,若原文件空,读写不到数据,非空,则读到指针后面剩下内容 |
      | w+ | 可读可写打开文件,文件不存在,创建文件;打开文件就清空文件,指针指向头部,所以读操作,指向文件头,返回空;可以写入,指针指向文件头部,覆盖原文件全部内容;读写同时进行,先读后写(打开文件就清空了内容),不管先读后写,还是先写后读(执行完写语句不会写入内容,执行完读操作(缓冲区内容到磁盘)才会写入成功),不会读到内容,总结不可读,但是使用读操作不报错 |
      | a+ | 可读可写打开文件,文件不存在,创建文件;可以读操作,不管是否存在内容,读取不到内容,返回空;可以写入,指针指向文件尾,追加写入;读写同时进行,不管先读后读(写入到缓冲区,执行完close语句写入磁盘),还是先写后读(执行完读语句写入),不会读到内容,总结不可读,但是使用读操作不报错 |
      | rb+ | 可读可写打开文件,文件不存在,创建文件;以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
      | wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
      | ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |

    • 读写:

      a)定位:

      # 偏移
      f.seek(偏移量[, 0, 1, 2])
      # 查看文件指针
      f.tell()

      # 通过命令行查看seek帮助文档
      f = open("py文件操作/a.txt", "r")
      help(f.seek)
      '''
      seek的第二个参数
      0.文件指针在文件头,偏移量为正
      1.文件指针在文件中,表示当前位置,有可能偏移量可以为负
      2.文件指针在文件尾部,偏移量为负

      注意:文本文件不带"b"seek第二参数只能写0,二进制文件(加b)操作模式下可以为1或2
      '''

      b)读:

      '''
      f.read(字节数)
      字节数默认是文本内容长度,下标自动移动
      * f.readline([limit])
      读取一行数据,limit限制最大读取字节数
      f.readlines()
      自动将文件按照换行符处理,将处理好的每一行数据组成列表返回

      * for in (动态加载内存)
      1.遍历行列表
      2.遍历文件本身(文件对象就是迭代器)

      判断文件是否可读(容错处理)
      file对象.readable()

      注意:
      文件比较大时使用readline方法,按行加载,可节省内存,但相对于其他两个读取方法,性能较低
      其他两个方法一次读取全部内容,虽然占用内存,但处理性能比较高
      '''

      c)写:

      '''
      file对象.write()
      返回写入字节的长度

      判断文件是否可写(容错处理)
      file对象.writable()
      '''
  • 关闭:

    '''
    file对象.close()
    关闭一个打开的文件
    可以释放系统资源,会立即清空缓冲区的数据内容到磁盘文件

    file对象.flush()
    将缓冲区的数据内容刷新到磁盘文件中
    '''
  1. 文件常用操作

    import os

    # 重命名文件 os.rename(原, 新), 没有文件或目录报错
    # os.rename("b.txt", "one.txt")

    # 重命名目录
    # os.rename("first", "one")

    # 同时修改目录名和文件名 renames()
    # os.renames("one/one.txt", "two/two.txt")

    # 删除文件,不存在报错 remove()
    # os.remove("m52.jpg")

    # 删除目录,文件夹非空报错 rmdir()
    # os.rmdir("one")

    # 只删除two目录
    # os.rmdir("one/two")

    # 递归删除目录,文件夹非空报错 removedirs()
    # os.removedirs("one/two")

    # 创建一级目录 mkdir()
    # os.mkdir("one")

    # 无法递归创建多级目录,报错
    # os.mkdir("a/b/c")

    os.mkdir("a", 0o777)
    '''
    数字权限模式,windows忽略,可以参照linux的文件权限(读r:4、写w:2、可执行x:1)
    文件拥有者 八进制第一位数(读r:4、写w:2、可执行x:1)
    同组用户 八进制第二位数(读r:4、写w:2、可执行x:1)
    其他用户 八进制第三位数(读r:4、写w:2、可执行x:1)
    '''

    # 切换默认目录,os.chdir(target目录),目标目录不存在会报错
    # os.chdir("one")

    # open("ddd.txt", "w")
    # 获取当前目录 os.getcwd()
    # print(os.getcwd())

    # 获取目录内容列表 os.listdir(path) 目录默认是当前目录下("./") 返回一个列表
    print(os.listdir("../"))
  2. 案例:

    # 文件复制案例
    import os

    os.chdir("../file/")
    # 1.以只读模式打开要复制的文件
    # 以追加模式打开副本文件
    # 两个文件编码一致,不会乱码
    source_file = open("poem.txt", "r", encoding="utf-8")
    target_file = open("poem_copy.txt", "a", encoding="utf-8")

    # 2.从原文件中读取内容
    # 目标文件写内容
    # if source_file.readable():
    # content = source_file.read()
    # if target_file.writable():
    # target_file.write(content)

    while True:
    content = source_file.read(1024)
    if len(content) == 0:
    break
    target_file.write(content)

    # 刷新流
    target_file.flush()

    # 3.关闭流
    source_file.close()
    target_file.close()


    '''--------------------------------------------'''
    # 创建于扩展名相同目录并移动到该目录下,进行分类
    # 获取文件名称列表
    import os
    import shutil

    path = "../file"
    if not os.path.exists(path):
    exit()

    os.chdir(path)
    file_list = os.listdir("./")
    # print(file_list)
    # 1 遍历所有文件
    for file_name in file_list:
    # print(file_name)
    # 2 分解文件后缀名
    # 2.1获取最后一个.的索引位置
    index = file_name.rfind(".")
    # print(index)
    # 2.2根据这个索引开始截取后续内容
    extension = file_name[index + 1:]
    # print(extension)
    if extension == -1:
    continue

    # 3 查看一下, 是否存在同名的目录
    # 4 如果不存在这样的目录,创建该名称的目录
    if not os.path.exists(extension):
    # 创建
    os.mkdir(extension)
    # 移动
    # 5 目录存在后,移动对应文件
    shutil.move(file_name, extension)



    '''------------------------------------'''
    # 打印文件目录文件夹及文件清单
    # 获取文件名称列表
    import os
    import shutil

    # path = "../file"
    # if not os.path.exists(path):
    # exit()
    # listdir = os.listdir(path)
    # print(listdir)

    def listFiles(dir, f):
    file_list = os.listdir(dir)
    # print(file_list)
    for file_name in file_list:
    new_file_name = dir + "/" + file_name
    # 判断是不是目录
    if os.path.isdir(new_file_name):
    # print(new_file_name)
    f.write(new_file_name + "\n")
    # 是目录,递归
    listFiles(new_file_name, f)
    else:
    # print("\t" + file_name)
    f.write("\t" + file_name + "\n")

    # print("")
    f.write("\n")


    f = open("list.txt", "a")
    listFiles("../file", f)

二、py核心知识点(oop)

  • python面向对象(Object-oriented programming (OOP) )

1.面向对象基本理论

  • 面向对象:解决问题时关注的是需要解决问题所需要的的对象,根本是不同具体对象面向过程的封装
  • 面向过程:解决问题时关注解决问题的每一个步骤(过程)
  • 类:某一个对象的特征行为的抽象,包特征(属性)和行为(方法)
  • 实例化对象:拥有某个类的属性和方法,且属性方法得到具体实现

2.面向对象在python中实现

2.1 定义一个类(类也是对象,万物皆对象)

class 类名:
pass
# 类名驼峰命名法,与java一致
# 类名也可以作为变量,可以给他赋值

2.2 实例化对象

对象名 = 类名()

2.3 属性:某个对象的特性,通过实例化对象来调;而变量是可以改变的量值

'''
对象(实例)属性(java没有对象属性概念,python有)
* 使对象具有一些属性(增)
1.直接通过对象,动态添加,对象.属性 = 值
2.通过类的初始化方法(构造方法) __init__方法(当我们创建好一个对象后,会自动调用该方法,初始化对象)
* 访问对象的属性(查)
对象.属性
查看对象属性是否添加成功:打印对象.属性,或者获取所有该对象属性,对象.__dict__
* 修改对象的属性(改)
对象.属性 = 新值
修改操作,属性会指向新的空间(不可变类型),可变类型的值会修改值,指向的内存地址不变
* 删除对象的属性(删)
del 对象.属性
* 对象名访问属性,其实是访问对象的__dict__属性指向地址的属性值,可以通过(对象.__dict__ = 字典)给对象设置属性,说明了__dict__属性可以修改

可以通过对象.__class__ = 类名 来修改所属类

类属性(类本身、及根据该类创建的对象都可以共享)
* 使类具有一些属性(增)
1.直接通过类名,动态添加,类名.属性 = 值
2.在类的代码块直接写属性名 = 值
* 访问类的属性(查)
类名.属性
查看类属性是否添加成功:打印 类名.属性,或者获取所有该类属性,类名.__dict__
* python对象属性查找机制,python对象会优先查找对象自身属性,找不到才根据__class__去对象对应的类属性查找
* 修改类的属性(改)
类名.属性 = 新值
修改操作,属性会指向新的空间(不可变类型),可变类型的值会修改值,指向的内存地址不变
* 属于该类创建的对象修改类属性,成为对象自己的属性,属于新增操作,但不会修改类属性
* 删除类的属性(删)
del 类名.属性
不能使用del 对象.属性,会报错,即不能通过对象删除类属性


通过设置__slots__属性(在类的内部设置)
列表中的元素,即为通过这个类创建出的对象可以添加的对象属性
如果这个类实例出的对象,添加了非列表之内的属性,则会报错

'''

2.4 方法:描述一个目标的行为动作

'''
方法与普通函数异同:
都封装了一系列行为动作
都可以被调用的之后,执行一系列行为动作
最主要的区别就是:调用方式,方法需要对象调


方法的划分:
* 实例方法
默认第一个参数需要接收到一个实例
* 类方法(加@classmethod注解,原理就是装饰器)
默认第一个参数需要接收到一个类
* 静态方法(加@staticmethod注解)
第一个参数什么都不默认接收
* 注意:
1.划分的依据是:方法的第一个参数必须要接收的数据类型
2.不管是哪一种类型的方法,都是存储在类当中;没有在实例当中的
3.不同类型方法的调用方式不同
'''

2.5 实例方法:可以使用实例属性和类属性 self.属性

'''
class 类名:
def 方法名(self):
方法体

标准调用:
使用实例调用实例方法,不用手动传参,解释器默认将当前实例本身传递过去
注意:至少有一个参数,不然报错,一般形参用self,也可以自定义
其他调用:
使用类调用: 类名.方法名(参数列表)
间接调用: 函数名 = 类名.方法名 函数名(参数列表)
本质就是找到函数本身取调用它
'''

2.6 类方法:可以使用类属性 cls.类属性(没有传实例对象参数前提)

'''
class 类名:
@classmethod
def 方法名(cls):
方法体

类调用:不用手动传递第一个参数,会自动的把调用的类本身给传递过去(一般使用类调用)
实例调用:不用手动传递第一个参数,会自动的把调用的对象对应的类给传递过去
'''

2.7 静态方法:可以使用类属性,类名.属性(没有传实例对象参数前提)

'''
class 类名:
@staticmethod
def 方法名():
方法体

类调用:不用传递参数
实例调用:不用传递参数
看场景使用什么调用方法
'''

2.8 元类:创建类对象的类(type类,继承于object)

类的关系

'''
另一种创建类方式:使用type创建类
自定义的类变量 = type(类名, 元组, 字典{属性键值对或方法键值对})
实例 = 自定义的类变量()
'''

image-20221004221037180

2.9 类创建流程

'''
类创建流程:
1. 检测类中是否有明确 __metaclass__属性,有, 则通过指定元类来创建这个类对象
2. 检测父类中是否存在__metaclass__属性,有, 则通过指定元类来创建这个类对象
3. 检测模块中是否存在__metaclass__属性,有, 则通过指定元类来创建这个类对象
4. 通过内置的type这个元类,来创建这个类对象
4. 通过内置的type这个元类,来创建这个类对象
场景:1) 拦截类的创建 2) 修改类 3) 返回修改之后的类
'''

2.10 类的描述

  • 三个双引号对“”“类、方法描述”“”,属性的描述写在类描述里面,具体参考官方文档
'''
方式1:
使用内置模块pydoc
具体步骤
1.查看文档描述
python -m pydoc 模块名称
2.启动本地服务, 浏览文档
python -m pydoc -p 1234
# 自动生成端口号
python -m pydoc -b
3.生成指定模块html文档
python -m pydoc -w 模块名称
方式2
使用三方模块
Sphinx
epydoc
doxygen
'''

2.11 属性相关补充

2.11.1 私有化属性
'''
私有化属性
概念
是指将一些原本公开的属性设置权限, 只能小范围访问, 其他地方访问不了
意义
保证数据的安全性
提高代码的可维护性
* 注意
Python并没有真正的私有化支持,但是, 可以使用下划线完成伪私有的效果
类属性(方法)和实例属性(方法)遵循相同的规则
x
公有属性
类内部访问
子类内部访问
模块内其他位置访问
类访问
父类
派生类(继承的类)
实例访问
父类实例
派生类实例
跨模块访问
import形式导入
from 模块 import * 形式导入
_y
受保护属性
类内部访问
子类内部访问
模块内其他位置访问(有警告,但是可以访问)
类访问
父类
派生类
实例访问
父类实例
派生类实例
跨模块访问
import形式导入(有警告,但是可以访问)
from module import * 形式导入
* 有__all__指明对应变量(提前使用__all__ = ["_y"],可以访问,不警告)
* 没有__all__指明对应变量(报错,不能访问)
__z
私有属性
类内部访问
子类内部访问(报错,不能访问)
模块内其他位置访问(报错,不能访问)
类访问
父类
派生类
实例访问
父类实例
派生类实例
跨模块访问(属性报错,不能访问,模块可以按照下面方案访问)
import形式导入(可以访问)
from module import * 形式导入
* 有__all__指明对应变量(提前使用__all__ = ["_y"],可以访问,不警告)
* 没有__all__指明对应变量(报错,不能访问)
私有属性的实现机制
名字重整(Name Mangling)
重改__x为另外一个名称, 如
_类名__x
目的
防止外界直接访问
防止被子类同名称属性覆盖
应用场景
数据保护
数据过滤
补充
xx_
"变量名_" 这个格式是为了与系统属性作区分
__xx__
两端__一般为系统内置属性或方法, 所以以后命名注意避免
'''
2.11.2 只读属性
'''
只读属性
概念
一个属性(一般指实例属性), 只能读取, 不能写入
应用场景
有些属性, 只限在内部根据不同场景进行修改, 而对外界来说, 不能修改, 只能读取
比如
电脑类的网速属性, 网络状态属性
方式1
方案
全部隐藏
私有化
既不能读
也不能写
部分公开
公开读的操作(设置方法返回私有属性)
具体实现
私有化
通过"属性前置双下划线"实现
部分公开
通过公开的方法
优化(加装饰器@property)
property
作用
将一些"属性的操作方法"关联到某一个属性中
概念补充(使用__bases__属性查看继承的基类)
经典类
没有继承(object)
新式类
继承(object)
Python2.x版本定义一个类时, 默认不继承(object)
Python3.x版本定义一个类时, 默认继承(object)
建议使用
新式类
property
在经典类中
只能管理一个属性的读取操作
在新式类中
可以管理一个属性的删改查操作
方式2
方案
借助系统内置的方法进行拦截
具体实现
__setattr__方法
当我们使用 "实例.属性 = 值" 这种格式给一个实例增加或修改属性的时候, 都会调用系统内置的这个方法
在这个方法的内部, 才会真正的把属性以及对应的值给存储到 __dict__当中
解决方案
在这个方法中, 进行拦截
'''
# 在python3.x环境下
# property在新式类使用方式一
class Person(object):
def __init__(self):
self.__age = 18

def set_age(self, age):
print("setAge...")
self.__age = age

def get_age(self):
print("getAge...")
return self.__age

# 参数顺序不能反
age = property(get_age, set_age)


# p = Person()
# p.age = 666
# print(p.age)
# print(p.__dict__)

# property在新式类使用方式二
class Animal(object):
def __init__(self):
self.__age = 15

@property
def age(self):
print("get___________")
return self.__age

@age.setter
def age(self, age):
print("set__________")
self.__age = age


animal = Animal()
animal.age = 666
print(animal.age)
print(animal.__dict__)


# python2.x环境下测试
# property在经典类使用方式一
class Person:
def __init__(self):
self.__age = 18

def set_age(self, age):
print "setAge..."
self.__age = age

def get_age(self):
print "getAge..."
return self.__age

# 参数顺序不能反
age = property(get_age, set_age)


# p = Person()
# # 经典类不能修改属性,只会新增属性,只关联了property的读取方法
# p.age = 666
# print p.age
# print p.__dict__


# property在经典类使用方式二
class Animal:
def __init__(self):
self.__age = 15

@property
def age(self):
print "get___________"
return self.__age

@age.setter
def age(self, age):
print "set__________"
self.__age = age

animal = Animal()
# # 经典类不能修改属性,只会新增属性,只关联了property的读取方法
animal.age = 666
print animal.age
print animal.__dict__


# 只读属性的方式二
class Person:
# 当我们使用 "实例.属性 = 值" 这种格式给一个实例增加或修改属性的时候, 都会调用系统内置的这个方法
# 在这个方法的内部, 才会真正的把属性以及对应的值给存储到 __dict__当中
def __setattr__(self, key, value):
print(key, value)
if key == "age" and key in self.__dict__.keys():
print("只读属性,不能设置属性")
else:
# self.key = value 死循环
self.__dict__[key] = value

p = Person()
p.age = 15
print(p.age)
# p.name = "zz"

p.age = 66
print(p.age)
print(p.__dict__)

'''
2.11.3 内置特殊属性
# 内置特殊属性
# 类属性
# __dict__ : 类的属性
# __bases__ : 类的所有父类构成元组
# __doc__ :类的文档字符串
# __name__: 类名
# __module__: 类定义所在的模块
# 实例属性
# __dict__ : 实例的属性
# __class__: 实例对应的类

2.12 私有化方法

'''
私有方法(与私有化属性类似)
def __方法():
pass
注意
不要定义 "_类名__方法名" 这种方法,会把名字重整机制的方法覆盖掉
'''

2.13 内置特殊方法

2.13.1 生命周期方法
2.13 .2 其他内置方法
# ----------------信息格式化操作:__str__方法-------------------------

# class Person:
# # 类的构造方法,self相当于java的this关键字
# def __init__(self, weight, height):
# self.weight = weight
# self.height = height
#
# # 相当于java自己重写的toString()方法
# def __str__(self):
# # return "这个人的身高是%s, 年龄是%s" % (self.height, self.weight)
# # return "这个人的身高是%(height)s, 年龄是%(weight)s" % ({"height": self.height, "weight": self.weight})
# # return "这个人的身高是{0}, 年龄是{1}".format(self.height, self.weight)
# return f"这个人的身高是{self.height}, 年龄是{self.weight}"
#
#
# p1 = Person(40, 152)
# print(p1.height)
# print(p1.weight)
# print(p1)
#
#
# p2 = Person(70, 190)
# print(p2.height)
# print(p2.weight)
# print(p2)
#
# s = str(p1)
# print(s, type(s))


# ----------------信息格式化操作:__repr__方法-------------------------

# class Person:
# # 类的构造方法,self相当于java的this关键字
# def __init__(self, weight, height):
# self.weight = weight
# self.height = height
#
# # 相当于java自己重写的toString()方法
# def __str__(self):
# # return "这个人的身高是%s, 年龄是%s" % (self.height, self.weight)
# # return "这个人的身高是%(height)s, 年龄是%(weight)s" % ({"height": self.height, "weight": self.weight})
# # return "这个人的身高是{0}, 年龄是{1}".format(self.height, self.weight)
# return f"这个人的身高是{self.height}, 年龄是{self.weight}"
#
# def __repr__(self):
# return "vvv"
#
# p1 = Person(40, 152)
# print(p1)
#
#
# p2 = Person(70, 190)
# print(p2)
#
# # s = str(p1)
# # print(s, type(s))
#
# print(repr(p1))


# import datetime
#
# t = datetime.datetime.now()
# print(t)
# tmp = repr(t)
# # 面向开发人员
# print(tmp)
# # 面向用户
# print(eval(tmp))


# ----------------调用操作: __call__方法-------------------------

# class Person:
# def __call__(self, *args, **kwargs):
# print("xxx", args, kwargs)
#
# pass
#
#
# p = Person()
# p(12, 56, name="jj")
import collections.abc
import functools


# 关键字参数放后面
# def getColorPen(pen_color, pen_type):
# print(f"{pen_type}的颜色是{pen_color}")
#
#
# pen = functools.partial(getColorPen, pen_type="钢笔")
# pen("红色")
# pen("绿色")
# pen("蓝色")


# class PenFactory:
# def __init__(self, pen_type):
# self.pen_type = pen_type
#
# def __call__(self, pen_color):
# print(f"{self.pen_type}的颜色是{pen_color}")
#
#
# p = PenFactory("钢笔")
# p("红色")
# p("绿色")
# p("蓝色")
# pencil = PenFactory("铅笔")
# pencil("红色")
# pencil("绿色")
# pencil("蓝色")


# ----------------索引操作-------------------------
# class Person:
# def __init__(self):
# self.cache = {}
#
# def __setitem__(self, key, value):
# # print("__setitem__", key, value)
# self.cache[key] = value
#
# def __getitem__(self, item):
# # print("__getitem__", item)
# return self.cache[item]
#
# def __delitem__(self, key):
# # print("__delitem__", key)
# del self.cache[key]
# pass
#
#
# person = Person()
# person["name"] = "zhangsan"
# print(person["name"])
# del person["name"]
# # print(person["name"])
# print(person.cache)


# ----------------索引操作-------------------------
# class Person:
# def __init__(self):
# self.items = [1, 2, 3, 5, 6, 8, 9]
#
# def __setitem__(self, key, value):
# # 判断是不是切片对象
# if isinstance(key, slice):
# self.items[key.start: key.stop: key.step] = value
#
# def __getitem__(self, item):
# # print("get", item)
# if isinstance(item, slice):
# return self.items[item.start:item.stop:item.step]
#
# def __delitem__(self, key):
# # print("del", key)
# del self.items[key]
#
#
# p = Person()
# p[0:4:2] = ["l", "m"]
# print(p[0:4:2])
# del p[0:4:2]
# print(p.items)

# ----------------比较操作-------------------------
# python 2.x 可以直接使用 > < >= <= != ==比较
# python3.x需要实现六个方法才能使用,实现逻辑,但是方法不会叠加操作,不如eq方法与lt方法不会叠加为le方法
# class Person:
# def __init__(self, age, height):
# self.age = age
# self.height = height
#
# # """
# # 等于,可以由__ne__通过调换参数反推导出来,也可以自己写
# # """
# #
# # def __eq__(self, other):
# # print("__eq__")
# # return self.age == other.age
# #
# # """
# # 不等于,可以由__eq__通过调换参数反推导出来,也可以自己写
# # """
# #
# # def __ne__(self, other):
# # print("__ne__")
# # return self.age != other.age or self.height != other.height
# #
# # """
# # 大于,可以由__lt__通过调换参数反推导出来,也可以自己写
# # """
# #
# # def __gt__(self, other):
# # print("__gt__")
# #
# """
# 小于,可以由__gt__通过调换参数反推导出来,也可以自己写
# """
#
# def __lt__(self, other):
# print("__lt__")
# # 当为大于号时参数调换
# print(self.age)
# print(other.age)
# return self.age < other.age
#
# # """
# # 于等于,可以由__le__通过调换参数反推导出来,也可以自己写
# # """
# #
# # def __ge__(self, other):
# # print("__ge__")
# #
# # """
# # 大于等于,可以由__ge__通过调换参数反推导出来,也可以自己写
# # """
# #
# # def __le__(self, other):
# # print("__le__")
#
#
# p1 = Person(10, 160)
# p2 = Person(19, 170)
# print(p1 < p2)
# print(p1 > p2)

# ----------------比较操作_补充-------------------------
# import functools
#
#
# # 加下面装饰器至少必须有六个方法的其中一个
# @functools.total_ordering
# class Person:
# def __eq__(self, other):
# print("eq")
# pass
#
# def __lt__(self, other):
# print("lt")
# return False
#
#
# p1 = Person()
# p2 = Person()
# print(p1 <= p2)
# # print(Person.__dict__)


# # ----------------上下文环境中的布尔值-------------------------
# class Person:
# def __init__(self):
# self.age = 18
#
# def __bool__(self):
# return self.age >= 18
#
#
# p1 = Person()
# if p1:
# print("实例为真")

# ----------------遍历操作-------------------------
# 遍历操作方式一
# class Person:
# def __init__(self):
# self.res = 1
#
# def __getitem__(self, item):
# self.res += 1
# if self.res >= 6:
# raise StopIteration("停止遍历")
# return self.res
#
#
# p = Person()
# for i in p:
# print(i)


# 遍历操作方式二
# class Person:
# def __init__(self):
# self.res = 1
#
# def __iter__(self):
# print("iter")
# # 返回迭代器
# # return iter([1, 2, 3, 4, 6])
# return self
#
# def __next__(self):
# self.res += 1
# if self.res >= 6:
# raise StopIteration("停止遍历")
# return self.res
#
# p = Person()
# # for i in p:
# # print(i)
# print(next(p))
# print(next(p))
# print(next(p))
# print(next(p))
# # print(next(p))


# ----------------遍历操作,恢复迭代器初始值-------------------------
# class Person:
# def __init__(self):
# self.res = 1
#
# # 方式一
# # def __getitem__(self, item):
# # self.res += 1
# # if self.res >= 6:
# # raise StopIteration("停止遍历")
# # return self.res
#
# # 方式二
# def __iter__(self):
# # 每次迭代重新赋值,使得迭代器重用
# self.res = 1
# return self
#
# # def __next__(self):
# # self.res += 1
# # if self.res >= 6:
# # raise StopIteration("停止遍历")
# # return self.res
#
# def __call__(self, *args, **kwargs):
# self.res += 1
# if self.res >= 6:
# raise StopIteration("停止遍历")
# return self.res
#
#
# p = Person()
# # 前提是实现了__getitem__方法,才可以使用iter()
# # pt = iter(p.__next__, 4)
# # 实例可以被调用,实现call方法
# pt = iter(p, 4)
# print(pt)
# print(p is pt)
# for i in pt:
# print(i)
#
# # for i in p:
# # print(i)
#
# # 可迭代对象或迭代器肯定可以for in,但是可以for in 不一定是可迭代对象或迭代器
# from collections.abc import *
#
# # 实例成为迭代器条件:写__iter__方法与__next__方法
# # 判断是不是迭代器对象
# print(isinstance(p, Iterator))
# # 判断是不是可迭代对象
# print(isinstance(p, Iterable))
# print(isinstance(pt, Iterator))
# print(isinstance(pt, Iterable))


# ----------------描述器定义方式一-------------------------
# class Person:
# def __init__(self):
# self.__age = 18
#
# @property
# def age(self):
# return self.__age
#
# @age.setter
# def age(self, value):
# if value < 0:
# value = 0
# self.__age = value
#
# @age.deleter
# def age(self):
# del self.__age
#
# num = "jj"
#
#
# person = Person()
# person.age = 10
# print(person.age)
# del person.age
# # print(person.age)
# help(person)


# ----------------描述器定义方式二-------------------------

# class Age(object):
# def __get__(self, instance, owner):
# print("get")
#
# def __set__(self, instance, value):
# print("set")
#
# def __delete__(self, instance):
# print("delete")
#
#
# class Person(object):
# age = Age()
# # def __getattribute__(self, item):
# # print("kkkk")
#
# # 只要新式类才会转换操作描述器
# person = Person()
# person.age = 10
# print(person.age)
# del person.age
# print(person.age)
# help(person)
# 只打印get。不会转换set和delete。所以一般通过实例操作描述器
# print(Person.age)
# Person.age = 20
# del Person.age


# 如果实现了__get__, 判定为"非资料描述器"
# 如果实现了__get__、 __set__,判定为"资料描述器"
# 资料描述器 > 实例字典 > 非资料描述器
# ----------------描述器优先级-------------------------

# class Age(object):
# def __get__(self, instance, owner):
# print("get")
#
# # def __set__(self, instance, value):
# # print("set")
# #
# # def __delete__(self, instance):
# # print("delete")
#
#
# class Person(object):
# age = Age()
#
# def __init__(self):
# self.age = 100
#
#
# # 只要新式类才会转换操作描述器
# person = Person()
# person.age = 10
# print(person.age)
# # del person.age
# print(person.__dict__)


# ----------------描述器值存在问题-------------------------

# class Age(object):
# def __get__(self, instance, owner):
# print("get")
# return instance.v
#
# def __set__(self, instance, value):
# print("set", self, instance, value)
# instance.v = value
#
# def __delete__(self, instance):
# print("delete")
# del instance.v
#
#
# class Person(object):
# age = Age()
#
#
# # 多个实例的age对象相同,通过instance来修改值
# person = Person()
# person.age = 10
# print(person.age)
# # del person.age
# person2 = Person()
# person2.age = 100
# print(person2.age)
# print(person.age)


# ----------------使用类实现装饰器-------------------------
# def check(func):
# def inner():
# print("登录验证")
# func()
#
# return inner

class Check:
def __init__(self, func):
self.func = func

def __call__(self, *args, **kwargs):
print("登录验证")
self.func()


# 语法糖装饰器 函数装饰器:@check 类装饰器:@Check
# @check
# @Check
def publish_novels():
print("发说说")


# 函数装饰器
# publish_novels = check(publish_novels)
# 类装饰器
publish_novels = Check(publish_novels)
publish_novels()
'''
内置特殊方法
1.生命周期方法[链接过去]
2.其他内置方法
a.信息格式化操作
__str__方法
作用
一个对象的描述字符串, 更加方便用户阅读, 对用户更友好
触发方式
print 打印一个对象时
str() 函数时
格式
def __str__(self):
return "描述信息"
__repr__方法
作用
一个对象的描述字符串, 更加方便机器处理, 对机器更友好(开发人员查看)
触发方式
当我们在交互模式下, 直接执行某个变量, 就会输出对应信息
repr() 函数时
格式
def __repr__(self):
return "描述信息"
注意
一般情况下, 应满足如下等式
obj == eval(repr(obj))
或者描述一个实例详细的信息(类名等等)
b.调用操作
__call__方法
作用
使得“对象”具备当做函数,来调用的能力
使用
1. 实现实例方法 __call__
2. 那么创建好的实例, 就可以通过函数的形式来调用
实例(参数)
应用场景
有点类似于之前所讲的"偏函数"的应用场景
可以将"常变参数"和"不常变参数"进行分离
案例
不同类型的笔, 画不同的图形
c.索引操作
作用
可以对一个实例对象进行索引操作
步骤
1. 实现三个内置方法
设置元素的方法
def __setitem__(self, key, value):
获取元素的方法
def __getitem__(self, item):
删除元素的方法
def __delitem__(self, key):
2. 可以以索引的形式操作对象
增/改
p[1] = 666
p["name"] = "sz"

p["name"]
p[1]

del p["name"]
del p[1]
d.切片操作
作用
可以对一个实例对象进行切片操作
步骤
Python2.x
1. 实现三个内置方法
__setspice__
设置某个元素切片时调用
__getspice__
获取某个元素切片时调用
__delspice__
删除某个元素切片时调用
2. 可以直接按照切片的方式操作对象
p[1, 6, 2]
* 注意: 过期
Python3.x
统一由"索引操作"进行管理
def __setitem__(self, key, value):
def __getitem__(self, item):
def __delitem__(self, key):
e.比较操作
作用
可以自定义对象 "比较大小, 相等以及真假" 规则
步骤
实现6个方法
相等
__eq__
不相等
__ne__
小于
__lt__
小于或等于
__le__
大于
__gt__
大于或等于
__ge__
注意
如果对于反向操作的比较符, 只定义了其中一个方法, 但使用的是另外一种比较运算, 那么, 解释器会采用调换参数的方式进行调用该方法
例如
定义了 "小于" 操作
x < y
使用 x > y
会被调换参数, 调用上面的 "小于操作"
但是, 不支持叠加操作
例如
定义了 "小于" 和 "等于" 操作
不能使用 x <= y
补充
使用装饰器, 自动生成"反向" "组合"的方法
步骤
1. 使用装饰器装饰类
@functools.total_ordering
2. 实现
> 或 >= 或 < 或 <= 其中一个
实现 ==
上下文环境中的布尔值
控制类中__bool__方法,返回布尔值来控制实例真假
f.遍历操作
怎样让我们自己创建的对象可以使用for in 进行遍历?
* 实现__getitem__方法
优先级低
每次for in 获取数据时, 都会调用这个方法
* 或者实现__iter__方法
优先级高
这个方法, 必须返回一个"迭代器";即, 具备"__iter__"和"__next__"方法
当for in 遍历这个对象时, 会调用这个__iter__方法;返回的迭代器对象的__next__方法
怎样让我们自己创建的对象可以使用next函数进行访问?
实现__next__方法
补充
1. __iter__方法可以恢复迭代器的初始化值, 复用迭代器
2. "可迭代" 与 "迭代器"必须实现的方法
3. iter方法的使用
描述器
概念
可以描述一个属性操作的对象
对象
属性的操作
增/改


描述
作用
可以代为管理一个类属性的读写删操作, 在相关方法中, 对数据进行验证处理, 过滤处理等等
如果一个类属性被定义为描述器,那么以后对这个类属性的操作(读写删), 都将由这个描述器代理
定义
定义方式1
property
定义方式2
三个方法
__get__
__set__
__delete__
调用细节
使用实例进行调用
最多三个方法都会被调用
使用类进行调用
最多会调用get方法
不能够顺利转换的场景
新式类和经典类
描述器仅在新式类中生效
方法拦截
一个实例属性的正常访问顺序
实例对象自身的__dict__字典
对应类对象的__dict__字典
如果有父类, 会再往上层的__dict__字典中检测
如果没找到, 又定义了__getattr__方法, 就会调用这个方法
而在上述的整个过程当中, 是如何将描述器的__get__方法给嵌入到查找机制当中?
就是通过这个方法进行实现
__getattribute__
内部实现模拟
如果实现了描述器方法__get__就会直接调用
如果没有, 则按照上面的机制去查找
注意
"资料描述器"和"非资料描述器"
如果实现了
_get__
判定为"非资料描述器"
如果实现了
__get__
__set__
判定为"资料描述器"
描述器和实例属性同名时, 操作的优先级问题
资料描述器 > 实例字典 > 非资料描述器
装饰器
使用类当做装饰器来使用

'''

2.14 浅拷贝与深拷贝

  • 变量的赋值操作:只是形成两个变量,实际上还是指向同一个对象

  • 浅拷贝:python拷贝一般都是浅拷贝,使用copy模块的copy函数拷贝时,对象包含的子对象内容不拷贝,因此源对象与拷贝对象会引用同一个子对象

  • 深拷贝:使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象与拷贝对象的所有子对象也不同

    # -----------------------深拷贝-----------------------------------
    class Cpu:
    def __init__(self, value):
    self.cpu = value


    class Disk:
    def __init__(self, value):
    self.disk = value


    class Computer:
    def __init__(self, cpu, disk):
    self.cpu = cpu
    self.disk = disk


    # print("--------------变量赋值---------------")
    # num1 = 12
    # num2 = num1
    # 同时指向一块内存空间
    # print(id(num1), id(num2))


    # print("--------------实例变量赋值---------------")
    # cpu = Cpu("英特尔")
    # disk = Disk("联想")
    # computer = Computer(cpu, disk)
    # 你要是认为是computer2没有实例化才会一模一样,你也可以实例化,发现就是一样的
    # computer2 = computer
    # print(computer, computer.disk, computer.cpu)
    # print(computer2, computer2.disk, computer2.cpu)

    print("--------------实例浅拷贝---------------")
    # import copy
    # cpu = Cpu("英特尔")
    # disk = Disk("联想")
    # computer = Computer(cpu, disk)
    # computer2 = copy.copy(computer)
    # # 只有子对象相同,指明子对象没有拷贝,对象本身拷贝了
    # print(computer, computer.disk, computer.cpu)
    # print(computer2, computer2.disk, computer2.cpu)

    print("--------------实例深拷贝---------------")
    import copy
    cpu = Cpu("英特尔")
    disk = Disk("联想")
    computer = Computer(cpu, disk)
    computer2 = copy.deepcopy(computer)
    # 对象本身与他的子对象都拷贝了
    print(computer, computer.disk, computer.cpu)
    print(computer2, computer2.disk, computer2.cpu)

3.python对象的生命周期,以及周期方法

3.1 生命周期概念

  • 指的是一个对象, 从诞生到消亡的过程
  • 当一个对象被创建时, 会在内存中分配相应的内存空间进行存储
  • 当这个对象不再使用, 为了节约内存, 就会把这个对象释放

3.2 涉及问题

  • 如何监听一个对象的生命过程?
  • Python是如何掌控一个对象的生命?

3.3 监听对象生命周期

返回

'''
__new__方法
当我们创建一个对象是, 用于给这个对象分配内存的方法
通过拦截这个方法, 可以修改对象的创建过程
比如:单例设计模式
__init__方法
每个对象实例化的时候,都会自动执行这个方法
可以在这个方法里面,初始化一些实例属性
__del__方法
当对象被释放的时候调用这个方法
可用于在这个方法中清理资源
'''

3.4 内存管理机制-存储方面

'''
1. 在Python中万物皆对象
* 不存在基本数据类型
* 0, 1.2, True, False, "abc",这些全都是对象
2. 所有对象, 都会在内存中开辟一块空间进行存储
* 会根据不同的类型以及内容, 开辟不同的空间大小进行存储
* 返回该空间的地址给外界接收(称为"引用"), 用于后续对这个对象的操作
a.可通过id()函数获取内存地址(10进制)
b.通过hex()函数可以查看对应的16进制地址
3. 对于整数和短小的字符, Python会进行缓存; 不会创建多个相同对象;此时, 被多次赋值, 只会有多份引用
4. 容器对象, 存储的其他对象, 仅仅是其他对象的引用, 并不是其他对象本身
* 比如字典, 列表, 元组这些"容器对象"
* 全局变量是由一个大字典进行引用;global()查看
'''

3.5 内存管理机制-垃圾回收方面

  1. 引用计数器机制

    • 概念
      一个对象, 会记录着自身被引用的个数,每增加一个引用, 这个对象的引用计数会自动+1,每减少一个引用, 这个对象的引用计数会自动-1
      
  2. 举例

    • 引用计数+1场景
      • 对象被创建
                p1 = Person()
        
      • 对象被引用
                p2 = p1
        
      • 对象被作为参数,传入到一个函数中
                log(p1)
                  这里注意会+2, 因为内部有两个属性引用着这个参数
        
      • 对象作为一个元素,存储在容器中
                l = [p1]
        
    • 引用计数-1场景
      • 对象的别名被显式销毁
                del p1
        
      • 对象的别名被赋予新的对象
                p1 = 123
        
      • 一个对象离开它的作用域
                 a.一个函数执行完毕时;
                       b.内部的局部变量关联的对象, 它的引用计数就会-1;
        
      • 对象所在的容器被销毁,或从容器中删除对象
  3. 查看引用计数

import sys
sys.getrefcount(对象) # **注意会大1,结果要减1**

  1. 垃圾回收机制

    • 主要作用:从经历过”引用计数器机制”仍未被释放的对象中, 找到”循环引用”, 干掉相关对象

    • 底层机制(了解&难)

      • 怎样找到”循环引用”?

                   1. 收集所有的"容器对象", 通过一个双向链表进行引用
                         容器对象
                             可以引用其他对象的对象
                                 列表
                                 元组
                                 字典
                                 自定义类对象
                                 ...
                         非容器对象
                             不能引用其他对象的对象
                                 数值
                                 字符串
                                 布尔
                                 ...
                             注意: 针对于这些对象的内存, 有其他的管理机制
                        2. 针对于每一个"容器对象", 通过一个变量gc_refs来记录当前对应的引用计数
                        3. 对于每个"容器对象",找到它引用的"容器对象", 并将这个"容器对象"的引用计数 -1
                        4. 经过步骤3之后, 如果一个"容器对象"的引用计数为0, 就代表这玩意可以被回收了, 肯定是"循环引用"导致它活到现在的
        
      • 如何提升查找”循环引用”的性能?

        • 如果程序当中创建了很多个对象, 而针对于每一个对象都要参与”检测”过程; 则会非常的耗费性能

        • 所以, 基于这个问题, 产生了一种假设:

                      a.越命大的对象, 越长寿
                            b.假设一个对象10次检测都没给它干掉, 那认定这个对象一定很长寿, 就减少这货的"检测频率"
          
        • 基于这种假设, 设计了一套机制

                      1.分代回收机制
          

          ​ a.默认一个对象被创建出来后, 属于 0 代

          ​ b.如果经历过这一代”垃圾回收”后, 依然存活, 则划分到下一代

          ​ c.”垃圾回收”的周期顺序为

          ​ 1)0代”垃圾回收”一定次数, 会触发 0代和1代回收
          ​ 2)1代”垃圾回收”一定次数, 会触发0代, 1代和2代回收

          ​ 2.查看和设置相关参数
          ​ import gc
          ​ print(gc.get_threshold())
          ​ gc.set_threshold(700, 10, 5)
          ​ 垃圾回收器当中, 新增的对象个数-消亡的对象个数 , 达到一定的阈值时, 才会触发, 垃圾检测

    • 垃圾回收时机(掌握&简单)

          1. 自动回收
       - 触发条件
                         开启垃圾回收机制
                             gc.enable()
                                 开启垃圾回收机制(默认开启)
                             gc.disable()
                                 关闭垃圾回收机制
                             gc.isenabled()
                                 判定是否开启
                         并且达到了垃圾回收的阈值
                             垃圾回收器中, 新增的对象个数和释放的对象个数之差到达某个阈值
                             涉及方法
                                 gc.get_threshold()
                                     获取自动回收阈值
                                 gc.set_threshold()
                                     设置自动回收阈值
            2. 手动回收
       - 触发条件
                     gc.collect()
                          执行一次垃圾回收(开关状态无效)                
      
  2. 测量对象的引用个数
    辅助工具

        objgraph
            http://mg.pov.lt/objgraph/
            xdot
            graphviz
    

3.6 面向综合案例

# 私有装饰器
import win32com.client


# 创建播报器对象(window系统适用)
# speaker = win32com.client.Dispatch("SAPI.SpVoice")
# 通过播报器对象,播报语音字符串
# speaker.Speak("你好,小谢")


class Calc:

# 注意这里需要有self参数
def __say_decorator(self, word):
# 创建播报器对象(window系统适用)
speaker = win32com.client.Dispatch("SAPI.SpVoice")
# 通过播报器对象,播报语音字符串
speaker.Speak(word)

def __create_say_decorator(operator_sign=""):
def say_decorator(func):
def inner(self, num):
self.__say_decorator(operator_sign + str(num))
return func(self, num)

return inner

return say_decorator

def __check_num_decorator(func):
def inner(self, num):
if not isinstance(num, int):
raise TypeError("类型错误,应该是一个整型数据")
return func(self, num)

return inner

@__check_num_decorator
@__create_say_decorator()
def __init__(self, num):
self.__res = num

@__check_num_decorator
@__create_say_decorator("加上")
def add_method(self, value):
self.__res += value
return self

@__check_num_decorator
@__create_say_decorator("减去")
def sub_method(self, value):
self.__res -= value
return self

@__check_num_decorator
@__create_say_decorator("乘以")
def mut_method(self, value):
self.__res *= value
return self

@__check_num_decorator
@__create_say_decorator("除以")
def div_method(self, value):
self.__res //= value
return self

# @__say_decorator
def show_value(self):
str1 = f"计算结果是{self.__res}"
self.__say_decorator(str1)
print(str1)
return self

def clear(self):
self.__res = 0
return self

@property
def result(self):
return self.__res


calc = Calc(100)
calc.add_method(56).sub_method(30).mut_method(3).div_method(120).show_value().clear().sub_method(200).show_value()
print(calc.result)

4.面向对象三大特性

4.1 封装

  • 封装概念:将一些属性和相关方法封装在一个对象中对外隐藏内部具体实现细节内部实现, 外界不需要关心,外界只需要根据”内部提供的接口”去使用就可以
  • 好处:
    1. 使用起来更加方便
    - 因为已经把很多相关的功能, 封装成一个整体
    - 类似于像外界提供一个工具箱
    - 针对于不同的场景, 使用不同的工具箱就可以
    2. 保证数据的安全
    - 针对于安全级别高的数据, 可以设置成"私有";
    - 可以控制数据为只读,外界无法修改
    - 也可以拦截数据的写操作,进行数据校验和过滤
    3. 利于代码维护
    - 如果后期, 功能代码需要维护, 则直接修改这个类内部代码即可; 
    - 只要保证接口名称不变; 外界不需要做任何代码修改
    
  • 案例

4.2 继承

概念:

  • 现实中的”继承:”子女继承父母的”财产资源”
  • 编程中的”继承”:一个类”拥有”另外一个类的”资源”的方式之一
    • “拥有”:并不是资源的复制, 变成双份资源,而是, 资源的”使用权”
    • “资源”:指”非私有的”属性和方法
  • 例子:Dog类继承自Animal类
    Dog:**子类、派生类**
    Animal:**父类、基类、超类**        
    
  • 目的:方便资源重用
4.2.1 继承的分类
  1. 单继承

    • 概念:仅仅继承了一个父类

    • 语法:

 
class Dog(Animal):
pass
  1. 多继承

    • 概念:继承了多个父类

    • 语法

 
class child(Father, Mather):
pass
4.2.2 type与object区分
  • type和object:

    type类与object类关系

  • 新式类:直接或者间接继承自object的类(python3.x没有显式继承object,但实际继承了object)

  • 经典类:没有直接或者间接继承自object的类(python2.x没有显式继承object,需要手动继承了object)
4.2.3 继承的影响
  1. 资源的继承

    • 明确:
      在Python中, 继承是指, 资源的使用权;所以, 测试某个资源能否被继承, 其实就是测试在子类当中, 能不能访问到父类当中的这个资源
      
    • 结论:
      除下私有的属性和私有的方法, 其他的基本都能继承
          公有属性/方法
          受保护属性/方法
          内置方法
      
  2. 资源的使

    • 继承的几种形态

      • 单继承链:一个子类只有一个父类

      • 无重叠的多继承链:继承链无交叉, 无公共父类

      • 有重叠的多继承链:继承链有交叉, 有公共父类

        image-20221006150741811

    • 几种形态应该遵循的标准原则

      • 单继承:遵循”从下到上的原则”,自己身上没有这个资源, 就到父类里面去找, 父类里面没有再往上找

      • 无重叠的多继承:遵循”单调原则”,按照继承的先后顺序, 优先调用左侧继承链上的资源

      • 有重叠的多继承:遵循”从下到上的原则”,简单理解就是A继承B继承C,B重写了C类的方法, 那么A优先使用B类的方法

        image-20221006151628533

    • 针对于几种标准原则的方案演化

      image-20221006164328361

    • 查看顺序:

    import inspect
    # 查看资源顺序三种方式
    print(inspect.getmro(A))
    print(A.mro())
    print(A.__mro__)
  1. 资源的覆盖(属性覆盖,方法重写

    • 原理:

      在MRO的资源检索链当中,优先级比较高的类写了一个和优先级比较低的类一样的一个资源(属性或方法),到时候, 再去获取相关资源, 就会优先选择优先级比较高的资源;,而摒弃优先级比较低的资源; 造成”覆盖”的假象。

    • 注意事项:当调用优先级比较高的资源时, 注意self的变化

  2. 资源的累加:在一个类的基础之上, 增加一些额外的资源

    子类相比于父类, 多一些自己特有的资源,直接添加

    在被覆盖的方法基础之上, 新增内容:

    • 方案一:在高优先级类的方法中, 通过”类名”调用低优先级类的方法,弊端是代码维护性差、容易产生重复调用;

    • 方案二:在低优先级类的方法中, 通过”super”调用高优先级类的方法

      • 概念:一个类,在新式类中有效

      • 作用:

        • 起着代理的作用, 帮我们完成以下任务
        • 沿着MRO链条, 找到下一级节点, 去调用对应的方法
      • 问题:沿着谁的MRO链条? 找谁的下一个节点? 如何应对类方法, 静态方法以及实例方法的传参问题?

      • 语法原理:

        • super(参数1[, 参数2]) 参数1:当前类,参数2为当前类获实例

        • 工作原理:

          def super(cls, inst):
          mro = inst.__class__.mro()
          return mro[mro.index(cls) + 1]
        • 问题解决:

          • 沿着参数2的MRO链条;
          • 找参数1的下一个节点;
          • 如何应对类方法, 静态方法以及实例方法的传参问题,使用参数2调用
      • 常用具体语法形式:

        # python2.2+
        super(type, obj) -> bound super object;
        # 需要链条的类与实例
        requires isinstance(obj, type)

        super(type, type2) -> bound super object;
        # 需要链条的类
        requires issubclass(type2, type)

        # python3+
        super()
      • 注意:

        • super 和父类(超类)没有实质性的关联,仅仅是沿着MRO链条, 找到下一级节点;
        • 保证调用形式的统一:要是类名调用, 全是类名调用;要是super调用, 全是super调用
        • 参数不要使用self.class,可能会死循环

4.3 多态

  • 一个类, 所延伸的多种形态
  • 在继承的前提下; 使用不同的子类, 调用父类的同一个方法, 产生不同的功能
  • 调用时的多种形态
  • 多态在Python中的体现:关注点在于对象的”行为和属性”; 而非对象的”类型”,与其他语言不同的是python的动态类型语言,只要对象有该方法,就可以调用,所以python没有真正意义上多态。

4.4 抽象类、抽象方法

  • 抽象类:抽象出来的类,没有具体的实现,不能创建实例,否则会报错
  • 抽象方法:抽象出来的方法,没有具体实现,不能直接被调用,需要子类实现(子类不实现会报错)
  • python中实现抽象:

    • 无法直接实现,需要导入模块 import abc
    • 设置类的元类为 abc.ABCMeta
    • 使用装饰器修饰抽象方法 @abc.abstractmethod
    • 抽象类方法@classmethod + @abc.abstractmethod等等
  • 具体抽象方法和属性实现查看官方文档

4.5 面向对象三大特性综合案例

class Animal(object):
def __init__(self, name, age=1):
self.name = name
self.age = age

def eat(self):
print(f"{self}吃饭")

def play(self):
print(f"{self}玩")

def sleep(self):
print(f"{self}睡觉")


class Dog(Animal):
def work(self):
self.__see_home()

def __see_home(self):
print(f"{self}在看家")

def __str__(self):
return f"名字是{self.name}, 年龄{self.age}岁的小狗在"


class Cat(Animal):
def work(self):
self.__catch_mouse()

def __catch_mouse(self):
print(f"{self}捉老鼠")

def __str__(self):
return f"名字是{self.name}, 年龄{self.age}岁的小猫"


class Person(Animal):
def __init__(self, name, pets, age=1):
super(Person, self).__init__(name, age)
self.pets = pets

def keep_pets(self):
for pet in self.pets:
pet.eat()
pet.play()
pet.sleep()

def make_pets_work(self):
for pet in self.pets:
pet.work()

def __str__(self):
return f"名字是{self.name}, 年龄{self.age}岁的天才"


dog = Dog("旺财", 12)
# dog.eat()
# dog.sleep()
cat = Cat("小花")
person = Person("xie", [dog, cat], 15)
print(person)
person.keep_pets()
person.make_pets_work()

5.py异常处理

5.1 异常和错误的概念

  • 错误:没法通过其他的代码进行处理的问题

    1. 语法错误

      • 比如定义函数写成了 dfe xxx()
      • 这种错误, 可以直接通过IDE或者解释器给出的提示进行修改
    2. 逻辑错误

      语法层面没有问题, 但是自己设计的逻辑出现问题
      例如
      用户输入年龄, 判定是否成年
      if age < 18:
      print("已经成年")
      这种错误, IDE或者解释器无法帮我们检测出, 只有我们通过代码测试进行排查
  • 异常:多指程序在执行的过程中, 出现的未知错误; 语法和逻辑都是正确的; 可以通过其他代码进行处理修复

    • 接收输入整型但输入了字符串,还强制转换,抛异常
    • 0作为除数,抛异常
    • 列表或字典,索引或键的异常等等

5.2 常见的系统异常

'''
除零异常
示例代码
1 / 0
异常名称
ZeroDivisionError
名称异常
示例代码
sz
异常名称
NameError
类型异常
示例代码
"1" + 2
异常名称
TypeError
索引异常
示例代码
l = [1, 2]
l[3]
异常名称
IndexError
键异常
示例代码
dic = {"name": "sz", "age": 18}
dic["add"]
异常名称
KeyError
值异常
示例代码
int("abc")
异常名称
ValueError
属性异常
示例代码
name = "sz"
print(name.xx)
异常名称
AttributeError
迭代器异常
示例代码
it = iter([1, 2])
print(next(it))
print(next(it))
print(next(it))
异常名称
StopIteration
'''
  • 系统异常类继承树:

    BaseException 所有内建的异常的基类:

    • SystemExit:由sys.exit()函数引发。当它不处理时,Python 解释器退出
    • KeyboardInterrupt: 当用户点击中断键 (通常ctrl + C) 时引发
    • GeneratorExit: 当调用一种generator的close()方法时引发。
    • Exception:所有内置的、 非系统退出异常是从该类派生的。应该从该类派生所有用户定义的异常。

5.3 解决异常

系统一开始已经内置了一些特定的应用场景; 当我们写代码的过程当中, 一旦触发了这个场景, 系统内部就会自动的向外界抛出这个问题, 也就是我们所谓的异常;不做处理,程序被终止执行; 软件的崩溃。

5.3.1 预防异常:添加容错代码

弊端:容错代码不属于我们主要的业务逻辑; 如果容错代码过多, 会造成代码混乱, 主业务不清晰

5.3.2 解决异常
  1. 方式一处理异常
方式一处理异常
try:
# 这里不管以后会抛出多少个异常, 只会从上往下检测, 检测到一个后, 就立即往下去匹配, 不会多次检测
可能会出现异常的代码
except 你要捕捉的异常类别 as(Python3使用as, python使用英文逗号) 接收异常的形参:
# 这一块可以有多个重复, 用于捕捉可能的其他异常; 如果针对于多个不同的异常有相同的处理方式, 那么可以将多个异常合并,通过写出元组合并
对于这个异常的处理
else:
# 这一块必须放在上一块except结束后(可以省略)
没出现异常时做的处理
finally:
# 这一块必须放最后(可以省略)
不管有没有出现异常, 都会执行的代码

注意:
try语句没有捕获到异常,先执行try代码段后,在执行else,最后执行finally
如果try捕获异常,首先执行except处理错误,然后执行finally
如果异常名称不确定, 而又想捕捉, 可以直接写Exception
  1. 方式二处理异常

    • 作用:适用于执行某一段代码A之前, 进行预处理, 执行代码A结束后, 进行清理操作;不管出现了什么异常, 最终都要执行一些清理操作

    • 语法:

      with context_expression [as target(s)]:
      with-body
  • 语法图解:

    语法土家

  • 示例:

    try:
    f = open("m5.jpg", "r")
    f.readlines()
    except Exception as e:
    print(e)
    finally:
    print("ccc")
    f.close()

    # 但是以上写法过于繁琐, 于是有了这个方案
    # 但是如果产生了异常, 依然会报错; 并没有进行异常处理操作
    with open("m5.jpg", "r") as f:
    f.readlines()

    3.自定义上下文管理器

    import traceback
    class Test(object):
    def __enter__(self):
    print("enter")
    return self

    def __exit__(self, exc_type, exc_val, exc_tb):
    print(self, exc_type, exc_val, exc_tb)
    print(traceback.extract_tb(exc_tb))
    print("exit")
    # True 不显示异常信息, False:显示异常信息
    return True

    with Test() as x:
    # print("body", x)
    1 / 0

    '''
    定义的类, 实现"上下文管理协议"
    __enter__
    做一些预处理操作
    __exit__
    做一些清理操作
    接收抛出的异常
    如果返回True, 则忽略异常; 如果返回的False, 则再次抛出异常
    '''
  1. contextlib 模块

  2. ```python
    import contextlib

@contextlib.contextmanager

def test():

# enter方法的具体实现

print(1)

# exit方法的具体实现

yield “kkkk”

print(2)

#
#

with test() as x:

print(3, x)

@contextlib.contextmanager
def ze():
try:
yield
except Exception as e:
print(“error”, e)

num1 = 1
num2 = 0

with ze():
num1 /num2

try:

num1 / num2

except Exception as e:

print(“666”)

a = 10
b = 0
with ze():
a / b

class Test:
def t(self):
print(“tttt”)

   # 必须写
   def close(self):
       print("资源释放")

   # 使用contextlib.closing取代
   # def __enter__(self):
   #     return self
   #
   # def __exit__(self, exc_type, exc_val, exc_tb):
   #     self.close()

with contextlib.closing(Test()) as test:
test.t()

python2.7

with open(“m5.jpg”, “rb”) as from_file:

with open(“m52.jpg”, “wb”) as to_file:

contents = from_file.read()

to_file.write(contents)

python3.x, 以后写法

with open(“m5.jpg”, “rb”) as from_file, open(“m52.jpg”, “wb”) as to_file:
contents = from_file.read()
to_file.write(contents)

python2.7之前

with contextlib.nested(open(“m5.jpg”, “rb”), open(“m52.jpg”, “wb”)) as (from_file, to_file):

contents = from_file.read()

to_file.write(contents)

‘’’
@contextlib.contextmanager
使用装饰器, 让一个生成器变成一个”上下文管理器”
contextlib.closing
这个函数, 让一个拥有close方法但不是上下文管理器的对象变成”上下文管理器”
contextlib.nested
python2.7之前: 完成多个上下文管理器的嵌套
‘’’


#### 5.4 抛出异常

根据严重程度是抛出异常还是处理解决异常,通过 raise 语句直接抛出相关类型的异常

```python
def setAge(age):
if age <= 0 or age > 200:
raise ValueError("值错误")
else:
print(age)


try:
setAge(-2)
except Exception as e:
print(e)

5.5 自定义异常(继承Exception, 或者BaseException)

class MyException(Exception):
def __init__(self, msg, error_code=200):
self.msg = msg
self.error_code = error_code

def __str__(self):
return self.msg + ":" + str(self.error_code)

def setAge(age):
if age <= 0 or age > 200:
raise MyException("年龄不符合,值错误", 404)
else:
print(age)


try:
setAge(-2)
except MyException as e:
print(e)

6.包和模块

  • 模块:将某一组相关功能的代码写入一个单独的.py文件中,这个.py文件就称为一个模块。
  • 包:包是一个有层次的目录结构,它定义了由n个模块和n个子包(目录下含有init.py文件的,还有模块和其他子包)
  • 库:具有一定功能的集合,可以是包或模块
  • 框架:从库的角度来看,解决一个开放性问题而设计的具有一定约束性的支撑结构
  • 作用:有效的对程序分解,方便维护和管理,供给他人使用;防止同一模块下重名的问题

6.1 包和模块的分类

6.1.2 标准包/模块
  1. 安装python后自动安装好一些模块,可以直接导入使用
  2. 特殊:内建包/模块,python自动帮我们导入导入的模块 builtins,当我们使用模块的内容时不需要手动导入模块,直接使用就行
6.1.3 三方包/模块

一些开发人员直接写的包,供给其他人下载使用

6.1.4 自定义包/模块

自己写的包/模块,发布出去就是第三方包/模块

6.2 包/模块的操作

6.2.1 包/模块的创建
  1. 创建模块:直接写一个.py文件就行
  2. 创建包:创建一个文件夹,含有init.py文件,该文件在python3.3后不用创建也可以,一般版本兼容都创建(第一次导入包是,自动执行init.py文件)
  3. 创建多级包:嵌套就行
6.2.2 包/模块的基本信息
  1. 包/模块的名称:模块名称为去掉.py的文件名,包名为文件夹名

  2. 包和模块存放的位置:

    import os
    # 打印当前文件路径
    print(os.path)
    # 打印os模块存放位置路径
    print(os.__file__)
  3. 查看包/模块的里面的内容

    print(dir(包名/模块名))
6.2.3 导入包/模块
  1. 常规导入(import语句导入)

    '''
    以下的包模块可以使用点语法定位
    1.导入单个模块
    import 同级目录的模块名/包名.模块名(注意包名可以嵌套,但是自己定义的模块与包使用这种方式导入只能导入当前py文件目录下的,不然报错找不到)

    2.导入多个模块
    import 同级目录的模块名/包名.模块名, 同级目录的模块名/包名.模块名...

    3.导入单个/多个模块并起别名(使用as关键字,与sql语法不同的是as不能省略)
    import 同级目录的模块名/包名.模块名 as 别名, 同级目录的模块名/包名.模块名 as 别名 ...

    注意:
    1.使用时,需要指定资源的模块名称,即写全,例如p1.tool_module.name
    2.如果仅仅导入一个包,在__init__.py再次导入相关模块,一般使用from... import...方式导入
    '''

    2.from… import语句导入

    '''
    语法:
    from A import B[ as 别名]
    A的资源必须包含B,即A顺序在前
    资源排序:
    包 > 模块 > 模块资源
    注意面向关系:
    包只能看到模块,看不到模块资源
    模块只能看到模块资源

    最终组合:
    1.从包中导入导入模块(包是可能有层级关系,模块没有层级,但可以多个,可以起别名)
    2.从模块中导入导入模块资源(模块是可能有层级关系(包有层级的),模块资源没有层级,但可以多个,可以起别名)

    注意:
    保持import后面最简化,即所有层级关系放在from后面;
    注意导入模块,模块是当前文件目录下的或文件目录的上级目录下的等等,需要写全包的路径名,即类似idea的全类名去掉类

    特例:
    1.from 模块 import *
    "*"代表所有非下划线_开头资源导入到当前位置(定义__all__看列表的,这个规定需要看列表有没有写入来判断)
    与在引入模块声明的__all__ = ["资源1", "资源2", ...]配合使用,代表"*"匹配到的资源,注意列表内都是字符串
    注意:慎用,避免当前位置与导入资源的变量冲突

    2.from 包 import *
    与在引入包的__init__.py中声明的__all__ = ["资源1", "资源2", ...]配合使用,代表"*"匹配到的模块,注意列表内都是字符串
    必须写__all__,没有什么都不导入
    '''
  2. 导入模块具体发生的事情:

    • 第一次导入:
      1. 在自己当下的命名空间中执行所有代码
      2. 创建一个模块对象,并将模块内所有顶级变量以属性的形式绑定在模块对象上
      3. 在import的位置,引入import后面的变量名称到当前命名空间
    • 第二次导入:
      • 直接执行上述第3步
    • 结论:
      • 注意:上述两种导入方式都大致执行以上步骤
      • 多次导入该模块,该模块不会执行多次
      • 两种导入方式没有区别是否节省内存,而是区别在于哪一部分内容拿到当前位置来用
  3. 从什么位置找到需要导入的模块:

    • 第一从导入:

      • 第一级:内置模块

      • 第二级:

        • sys.path构成:

          • 当前目录:

            import os
            print(os.getcwd())
          • 环境变量中PYTHONPATH(这个的路径是按照自己定义的)中指定的路径列表

          • 特定路径下的.pth文件中的文件路径列表

            1. 查看特定路径

              # 查看特定路径
              import site
              print(site.getsitepackages())
            2. 后缀名.pth,名字随意,一个路径占一行

          • 在python安装路径的lib库中搜索

        • 追加路径的方式:

          1. 直接追加到sys.path列表,只作用本次

          2. 修改环境变量

            • 修改PYTHONPATH,把需要的模块路径定义到环境变量(不用添加到path),仅仅在shell起作用
            • 在pycharm需要另外一种设置方式,设置里面的python Interpreter –> show ALL –>修改Interpreter path后点击ok,apply
          3. 添加.pth文件(在特定路径下创建该文件,并且文件内容的路径不能为中文)
    • 第二次导入:

      • 从已经加载过的模块去找

      • 查看已加载文件

        # 查看已加载文件
        import sys

        print(sys.modules)
  4. 导入模块的常见场景:

    • 局部导入:
      • 在某个局部范围内导入模块,在其他范围无法使用
      • 如果想要全局范围都能使用,在文件顶部导入相关模块
      • 使用场景:该模块使用不频繁,而且导入时间过长时
    • 覆盖导入:
      • 场景一:
        • 自定义模块与非内置的标准模块重名,根据前者存储位置,有可能会覆盖后者
        • 结论是自定义模块命名不要与后者重名
      • 场景二:
        • 自定义模块与内置模块重名,内置肯定覆盖自定义
        • 使用from 绝对路径 import…导入,覆盖内置,但还是不建议重名
    • 循环导入:

      • 假设有两个模块A、B,A模块导入了B模块,B模块导入了A模块,执行A模块或B模块都会造成循环导入(尽量避免使用,容易出现问题)
    • 可选导入

      • 概念:两个功能相似的包,根据需求优先导入选择其中一个导入
      • 场景:有两个A、B包,都可以实现相同功能,想优先使用A,而且需要做到没有A的情况下,B作为备选
      • 实现:使用try:… except …:…实现
    • 包内导入:python相对导入与绝对导入是基于包内导入而言的,包内导入就是包内导入包内部的模块

      • 绝对导入:

        • 参照sys.path路径进行检索

        • 例如指明包名或模块名

          import a
          from a import b
        • 注意:以上结论是基于python3.x以上

      • 相对导入:

        • 使用.来指明相对路径,.是根据模块名称获取的当前目录,..是根据模块名称所获取的上层目录

        • 例如:

          from . import a
          from .. import a
        • 注意:解释器是根据模块名称而确定层级关系,而不是存放目录,通过下划线下划线name下划线下划线查看模块名称。

      • 结论:

        • 包内导入:使用相对导入
        • 包外导入:使用绝对导入
        # __name__
        # 如果一个py文件直接以脚本形式执行 python file,它的名称就是__main__
        # 如果一个py文件使用模块的形式,进行加载的,那么它的名称由加载路径决定的,包名(顶级名称).子包名.模块名

        # import tool2
        # import tool2 python2.x 隐式相对导入等同于 from . import tool2
        # import tool2
        # 使用from __future__ import absolute_import禁用隐式相对导入
        from __future__ import absolute_import
        import tool2

6.3 三方包的安装与升级

7.虚拟环境

8.python反射

  • 具体含义看源码

  • import(name, globals=None, locals=None, fromlist=(), level=0):动态加载模块(import前后有双下划线)

  • hasattr(object, name, default=None):判断实例obj是否存在字符串name对应的属性

  • getattr(object, name, default=None):获取字符串name对应的属性

  • setattr(x, y, v):设置属性到实例

  • delattr(x, y):删除实例中字符串对应的属性,等同del x.y

  • 反射案例:

    # 案例一
    class User(object):

    def add_user(self):
    print("添加用户")

    def delete_user(self):
    print("删除用户")

    def update_user(self):
    print("更新用户")

    def select_user(self):
    print("查找用户")

    def before_process(self, method):
    if method == "add":
    self.add_user()
    elif method == "delete":
    self.delete_user()
    elif method == "update":
    self.update_user()
    elif method == "select":
    self.select_user()
    else:
    print("无效调用")

    def after_process(self, method):
    """
    去除ifelse的繁琐,类似理由转发
    """
    # p判断是否存在该方法
    if hasattr(self, method):
    # 获取方法
    user_method = getattr(self, method)
    user_method()
    else:
    print("无效调用")


    if __name__ == "__main__":
    # 没有处理,好多if
    User().before_process("delete")
    # 处理后
    User().after_process("delete_user")

    # 案例二
    # 保证按位置传参且只能传一个
    def new_method(self, /):
    print("我是新来的,给我一个实例")


    def main():
    """
    动态加载模块,创建类实例执行其中方法
    """
    # 动态导入模块Func
    func_module = __import__("Func", fromlist=True)
    print(func_module)

    # 判断是否找到对应类
    if hasattr(func_module, "Func"):
    # 通过模块对象实例获取类对象实例
    func_class = getattr(func_module, "Func")
    # print(type(func_class))
    print(func_class)

    # 判断是否存在方法
    if hasattr(func_class, "process"):
    # 通过类对象获取成员方法
    process_method = getattr(func_class, "process")
    # print(type(process_method))
    print(process_method)
    # 执行该方法
    process_method(func_class)
    else:
    print("没有找到当前方法")

    # 向类添加方法
    setattr(func_class, "newFunc", new_method)
    # 是否成功添加方法
    if hasattr(func_class, "newFunc"):
    # 获取该方法
    new_func = getattr(func_class, "newFunc")
    # 执行方法
    new_func(func_class)
    else:
    print("添加失败")

    # 删除Func类中法
    delattr(func_class, "newFunc")
    if not hasattr(func_class, "newFunc"):
    print("删除成功")
    else:
    print("没有找到该类")


    # 测试代码
    if __name__ == "__main__":
    main()