画布 1
第二模块
  数据类型
    推导式
      列表
        num_list = [ i for i in range(10)]
num_list = [ [i,i] for i in range(10)]
num_list = [ [i,i] for i in range(10) if i > 6 ]
      字典
        num_dict = { i:i for i in range(10)}
num_dict = { i:(i,11) for i in range(10)}
num_dict = { i:(i,11) for i in range(10) if i>7}
      集合(得到生成器)
        num_set = { i for i in range(10)}
num_set = { (i,i,i) for i in range(10)}
num_set = { (i,i,i) for i in range(10) if i>3}
      元素（生成器）
        # 不会立即执行内部循环去生成数据，而是得到一个生成器。
data = (i for i in range(10))
print(data)
for item in data:
print(item)
      高级补充
        推导式支持嵌套
          data = []
for i in range(5):
for j in range(10):
data.append( (i,j) )
          data = [ (i,j) for i in range(5) for j in range(10)]
          上面两种写法得出的结果是一样的
        坑面试题
          def num():
return [lambda x: i * x for i in range(4)]
# 1. num()并获取返回值 [函数,函数,函数,函数] i=3
# 2. for循环返回值
# 3. 返回值的每个元素(2)
result = [m(2) for m in num()] # [6,6,6,6]
print(result)
          def num():
return (lambda x: i * x for i in range(4))
# 1. num()并获取返回值 生成器对象
# 2. for循环返回值
# 3. 返回值的每个元素(2)
result = [m(2) for m in num()] # [0,2,4,6 ]
print(result)
          注意观察下面的两种情况，一个是存在列表中，一个是存在元组中，列表中的 i 可以变，而元组中的 i 就固定了 （注意存的地方是可变还是不可变即可）
    print输出进度百分比
      import time
print("开始下载")
for i in range(1, 101):
data = "\r{}%".format(i)
print(data, end="")
time.sleep(0.02)
print("\n下载完成")
  函数
    内置函数
      abs
        绝对值
      pow
        指数
          v1 = pow(2,5) # 2的5次方 2**5
      sum
        求和
          v1 = sum([-11, 22, 33, 44, 55]) #对数据的要求：可以被迭代-for循环
      divmod
        求商和余数
          v1, v2 = divmod(9, 2)
print(v1, v2) #4 1
      round
        小数点后n位（四舍五入）
          v1 = round(4.11786, 2)
print(v1) # 4.12
      min
        最小值
          v1 = min(11, 2, 3, 4, 5, 56)
print(v1) # 2
          v2 = min([11, 22, 33, 44, 55]) # 迭代的类型（for循环）
print(v2) #11
      max
        最大值（应用同上）
          v3 = max([-11, 22, 33, 44, 55], key=lambda x: x * 10)
print(v3) # 55 （取的结果是原结果）
      all
        是否全部为True
          v1 = all( [11,22,44,""] ) # False
      any
        是否存在True
          v2 = any([11,22,44,""]) # True
      bin，oct，hex，int
        进制转换（模块一有详细讲解）
      ord
        获取字符对应的unicode码点（十进制)
          v1 = ord("武")
print(v1, hex(v1))
      chr
        根据码点（十进制）获取对应字符
          v1 = chr(27494)
print(v1)
      float
        返回一个十进制浮点型数值（小数）
          print(float(18)) # 十进制的整数18 打印出十进制小数18.0
print(float(0x41)) # 十六进制整数41 打印出十进制小数65.0
print(float(0o41)) # 八进制整数41 打印出十进制小数33.0
print(float(0b1101)) # 二进制整数1101 打印出十进制小数13.0
print(float(True)) # 布尔值True 打印出十进制小数1.0
print(float(False)) # 布尔值False 打印出十进制小数0.0
print(float('32') # 表示十进制数字的字符串（其他字符串不行）
打印出十进制小数32.0
      str,bytes,bool,list,dict,tuple,set
        v2 = v1.encode('utf-8') # bytes类型
print(v2) #b'\xe6\xad\xa6\xe6\xb2\x9b\xe9\xbd\x90'
v3 = bytes(v1,encoding="utf-8") # bytes类型
print(v3) #b'\xe6\xad\xa6\xe6\xb2\x9b\xe9\xbd\x90'
      len，range,print,input,open(文件操作)，type（获取数据类型），id(以十进制展示)，hash
      enumerate
        自动编号
          v1 = ["武沛齐", "alex", 'root']
for num, value in enumerate(v1, 1):
print(num, value)
#输出
1 武沛齐
2 alex
3 root
      hash
        获取取一个对象（字符串或者数值等）的哈希值
      help
        获取帮助信息
          - pycharm，不用
- 终端，使用
      zip
        列表套列表格式
          v1 = [11, 22, 33, 44, 55, 66]
v2 = [55, 66, 77, 88]
v3 = [10, 20, 30, 40, 50]
result = zip(v1, v2, v3) #result是迭代器
for item in result:
print(item，type(item))
#结果
(11, 55, 10) <class 'tuple'>
(22, 66, 20) <class 'tuple'>
(33, 77, 30) <class 'tuple'>
(44, 88, 40) <class 'tuple'>
      callable
        是否可执行，后面是否可以加括号
          v1 = "武沛齐"
v2 = lambda x: x
def v3():
pass
print( callable(v1) ) # False
print(callable(v2)) # True
print(callable(v3)) # True
      sorted
        排序
          v1 = sorted([11,22,33,44,55]) #对原列表无影响，是重新赋值
v1 = sorted([11,22,33,44,55]，reverse = True) #倒序排列
    自定义函数
      函数入门
        函数定义
        参数
          形参
            定义有三个参数的函数（a1/a2/a3一般称为形式参数-形参）
          实参
            执行函数并传入参数（执行函数传值时一般称为实际参数-实参）
          传参
            位置传参
              def add(n1,n2):
print(n1+n2)
add(1,22)
            关键字传参
              def add(n1,n2):
print(n1+n2)
add(n1=1,n2=22)
            注意：当同时存在位置和关键字传参时，位置传参要在关键字传参的前面，否则会报错！
          动态参数
            一般用于无法确定参数数量时
            *
              只能按照位置传参，直接输出的话，就是元组
              def func(*args):
print(args) # 元组类型 (22,) (22,33,) (22,33,99,) ()
# 只能按照位置传参
func(22)
func(22,33)
func(22,33,99)
func()
            **
              只能按照关键字传参，直接输出的话，就是字典
              def func(**kwargs):
print(kwargs) # 字典类型 {"n1":"武沛齐"} {"n1":"武沛齐","age":"18","email":"xxxx"}
# 只能按关键字传参
func(n1="武沛齐")
func(n1="武沛齐",age=18)
func(n1="武沛齐",age=18,email="xx@live.com")
            *,**
              同时支持位置传参和关键字传参
              def func(*args, **kwargs):
print(args, kwargs)
func(22, 33, 99) # (22, 33, 99) {}
func(n1="武沛齐", age=18) # () {'n1': '武沛齐', 'age': 18}
func(22, 33, 99, n1="武沛齐", age=18) # (22, 33, 99) {'n1': '武沛齐', 'age': 18}
func() # () {}
            注意事项：（了解即可，不太重要）
              1. ** 必须放在 * 的后面
              2. 参数和动态参数混合时，动态参数只能放在最后
                def func2(a1, a2, a3, *args, **kwargs):
print(a1, a2, a3, args, **kwargs)
              3. 默认值参数和动态参数同时存在
                def func3(a1, a2, a3, a4=10, *args, a5=20, **kwargs):
print(a1, a2, a3, a4, a5, args, kwargs)
func3(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123)
            更深层次的补充：
              定义函数时可用 *和**
                def func(*args,**kwargs):
print(args,kwargs)
func("宝强","杰伦",n1="alex",n2="eric")
              执行函数时也可用 *和**
                形参固定，实参用 *和**
                  def func(a1,a2):
print(a1,a2)
func( 11, 22 )
func( a1=1, a2=2 )
func( *[11,22] )
func( **{"a1":11,"a2":22} )
                形参用 *和**，实参也用 *和**
                  def func(*args,**kwargs):
print(args,kwargs)
func( 11, 22 )
func( 11, 22, name="武沛齐", age=18 )
# 小坑，([11,22,33], {"k1":1,"k2":2}), {}
func( [11,22,33], {"k1":1,"k2":2} )
# args=(11,22,33),kwargs={"k1":1,"k2":2}
func( *[11,22,33], **{"k1":1,"k2":2} )
# 值得注意：按照这个方式将数据传递给args和kwargs时，数据是会重新拷贝一份的（可理解为内部循环每个元素并设置到args和kwargs中）。
              在使用format字符串格式化时，也可以这样用：
                v1 = "我是{},年龄：{}。".format("武沛齐",18)
v2 = "我是{name},年龄：{age}。".format(name="武沛齐",age=18)
v3 = "我是{},年龄：{}。".format(*["武沛齐",18])
v4 = "我是{name},年龄：{age}。".format(**{"name":"武沛齐","age":18})
          补充：
            函数执行传参时，传递的是内存地址
              ·def func(data):
print(data, id(data)) # 武沛齐 140247057684592
v1 = "武沛齐"
print(id(v1)) # 140247057684592
func(v1)
              好处：
                1：节省内存
                2：对于可变类型且函数中修改元素的内容，所有的地方都会修改。 可变类型：列表，字典，集合。
                  # 可变类型 & 修改内部修改 （只修改的话会改变原来的列表）
def func(data):
data.append(999)
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33,666]
                  # 特殊情况：可变类型 & 重新赋值 (像这样又给了一个内存地址【相当于重新赋值】，那么不会修改传入的那个)
def func(data):
data = ["武沛齐","alex"]
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33]
                  # 特殊情况：不可变类型，无法修改内部元素，只能重新赋值。
def func(data):
data = "alex"
v1 = "武沛齐"
func(v1)
            函数的返回值是内存地址
              def func():
data = [11, 22, 33]
return data
v1 = func()
print(v1) # [11,22,33]
              上述代码的执行过程：
- 执行func函数
- `data = [11, 22, 33]` 创建一块内存区域，内部存储[11,22,33]，data变量指向这块内存地址。
- `return data` 返回data指向的内存地址
- v1接收返回值，所以 v1 和 data 都指向 [11,22,33] 的内存地址（两个变量指向此内存，引用计数器为2）
- 由函数执行完毕之后，函数内部的变量都会被释放。（即：删除data变量，内存地址的引用计数器-1）
所以，最终v1指向的函数内部创建的那块内存地址。
            每次调用函数时，都会有不同的内存地址
              def func():
data = [11, 22, 33]
print(id(data))
return data
v1 = func()
print(v1, id(v1)) #2333376237120
v2 = func()
print(v2, id(v2)) # 2333375104960
            参数的默认值（很坑，也很有意思）
              原理：Python在创建函数（未执行）时，如果发现函数的参数中有默认值，则在函数内部会创建一块区域并维护这个默认值。
              正常的例子
                def func(a1,a2=18):
print(a1,a2)
                执行函数未传值时，则让a2指向 函数维护的那个值的地址。
                func("root")
                执行函数传值时，则让a2指向新传入的值的地址。
                func("admin",20)
              其他坑例子
                在特定情况【默认参数的值是可变类型 list/dict/set】 & 【函数内部会修改这个值】下，参数的默认值 有坑 。 （下面的例子都要注意每一次操作为默认参数带来的变化！）
                坑
                  # 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001
def func(a1,a2=[1,2]):
a2.append(666)
print(a1,a2)
# a1=100
# a2 -> 100010001
func(100) # 100 [1,2,666]
# a1=200
# a2 -> 100010001
func(200) # 200 [1,2,666,666]
# a1=99
# a2 -> 1111111101
func(99,[77,88]) # 66 [77,88,666]
# a1=300
# a2 -> 100010001
func(300) # 300 [1,2,666,666,666]
                大坑
                  # 在内部会维护一块区域存储 [1, 2, 10, 20,40 ] ,内存地址 1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
print(v1) # [1, 2, 10]
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
print(v2) # [1, 2, 10, 20 ]
# a1=30
# a2 -> 11111111111 [11, 22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
print(v3) # [11, 22,30]
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v4) # [1, 2, 10, 20,40 ]
                深坑
                  # 内存中创建空间存储 [1, 2, 10, 20, 40] 地址：1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
# a1=30
# a2 -> 11111111111 [11,22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v1) # [1, 2, 10, 20, 40]
print(v2) # [1, 2, 10, 20, 40]
print(v3) # [11,22,30]
print(v4) # [1, 2, 10, 20, 40] （这里一样的原因的本质就是他们传的是内存地址！！）
        返回值
          return 让函数执行一些功能后反馈一些结果回来
          1：返回值是任意类型，如果函数中（没有写return）或者（只写了个return）或者（写了return None），则默认返回都是None
            def func():
return [1,True,(11,22,33)]
result = func()
print(result) #[1,True,(11,22,33)]
            def func():
value = 1 + 1
ret = func()
print(ret) # None
          2：return后面的值如果有逗号，则默认会将返回值转换成元组再返回
            def func():
return 1,2,3
value = func()
print(value) # (1,2,3)
          3：函数一旦遇到return就会立即退出函数（终止函数中的所有代码）
            def func():
print(1)
for i in range(10):
print(i)
return 999
print(2)
result = func()
print(result)
# 输出
1
0
999
          返回值作用小结：
            1：完成某个结果并希望的到结果（在return中体现）
              def send_email():
...
return True
v1 = send_email()
              def encrypt(old):
...
return "密文..."
data = encrypt("武沛齐")
print(data)
            2：基于return控制让函数终止执行
              def func(name):
with open("xxx.txt",mode='r',encoding="utf-8") as file_object:
for line in file_object:
if name in line:
return True
data = func("武沛齐")
if data:
print("存在")
else:
print("不存在")
              def foo():
while True:
num = input("请输入数字（Q）：")
if num.upper() == "Q":
return
num = int(num)
if num == 99:
print("猜对了")
else:
print("猜错了，请继续！")
print("....")
foo()
        一些注意的点
          1：函数名的规范。（同变量名规范）
          2：函数的注释，说明函数的作用
            def encrypt(origin):
""" 用于数据加密和xxx """
pass
      函数进阶
        参数
          内存地址
            函数执行传参时，传递的是内存地址
              ·def func(data):
print(data, id(data)) # 武沛齐 140247057684592
v1 = "武沛齐"
print(id(v1)) # 140247057684592
func(v1)
              好处：
                1：节省内存
                2：对于可变类型且函数中修改元素的内容，所有的地方都会修改。 可变类型：列表，字典，集合。
                  # 可变类型 & 修改内部修改 （只修改的话会改变原来的列表）
def func(data):
data.append(999)
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33,666]
                  # 特殊情况：可变类型 & 重新赋值 (像这样又给了一个内存地址【相当于重新赋值】，那么不会修改传入的那个)
def func(data):
data = ["武沛齐","alex"]
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33]
                  # 特殊情况：不可变类型，无法修改内部元素，只能重新赋值。
def func(data):
data = "alex"
v1 = "武沛齐"
func(v1)
            函数的返回值是内存地址
              def func():
data = [11, 22, 33]
return data
v1 = func()
print(v1) # [11,22,33]
              上述代码的执行过程：
- 执行func函数
- `data = [11, 22, 33]` 创建一块内存区域，内部存储[11,22,33]，data变量指向这块内存地址。
- `return data` 返回data指向的内存地址
- v1接收返回值，所以 v1 和 data 都指向 [11,22,33] 的内存地址（两个变量指向此内存，引用计数器为2）
- 由函数执行完毕之后，函数内部的变量都会被释放。（即：删除data变量，内存地址的引用计数器-1）
所以，最终v1指向的函数内部创建的那块内存地址。
            每次调用函数时，都会有不同的内存地址
              def func():
data = [11, 22, 33]
print(id(data))
return data
v1 = func()
print(v1, id(v1)) #2333376237120
v2 = func()
print(v2, id(v2)) # 2333375104960
          默认参数的坑
            参数的默认值（很坑，也很有意思）
              原理：Python在创建函数（未执行）时，如果发现函数的参数中有默认值，则在函数内部会创建一块区域并维护这个默认值。
              正常的例子
                def func(a1,a2=18):
print(a1,a2)
                执行函数未传值时，则让a2指向 函数维护的那个值的地址。
                func("root")
                执行函数传值时，则让a2指向新传入的值的地址。
                func("admin",20)
              其他坑例子
                在特定情况【默认参数的值是可变类型 list/dict/set】 & 【函数内部会修改这个值】下，参数的默认值 有坑 。 （下面的例子都要注意每一次操作为默认参数带来的变化！）
                坑
                  # 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001
def func(a1,a2=[1,2]):
a2.append(666)
print(a1,a2)
# a1=100
# a2 -> 100010001
func(100) # 100 [1,2,666]
# a1=200
# a2 -> 100010001
func(200) # 200 [1,2,666,666]
# a1=99
# a2 -> 1111111101
func(99,[77,88]) # 66 [77,88,666]
# a1=300
# a2 -> 100010001
func(300) # 300 [1,2,666,666,666]
                大坑
                  # 在内部会维护一块区域存储 [1, 2, 10, 20,40 ] ,内存地址 1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
print(v1) # [1, 2, 10]
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
print(v2) # [1, 2, 10, 20 ]
# a1=30
# a2 -> 11111111111 [11, 22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
print(v3) # [11, 22,30]
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v4) # [1, 2, 10, 20,40 ]
                深坑
                  # 内存中创建空间存储 [1, 2, 10, 20, 40] 地址：1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
# a1=30
# a2 -> 11111111111 [11,22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v1) # [1, 2, 10, 20, 40]
print(v2) # [1, 2, 10, 20, 40]
print(v3) # [11,22,30]
print(v4) # [1, 2, 10, 20, 40] （这里一样的原因的本质就是他们传的是内存地址！！）
          *args,**kwargs
            定义函数时可用 *和**
              def func(*args,**kwargs):
print(args,kwargs)
func("宝强","杰伦",n1="alex",n2="eric")
            执行函数时也可用 *和**
              形参固定，实参用 *和**
                def func(a1,a2):
print(a1,a2)
func( 11, 22 )
func( a1=1, a2=2 )
func( *[11,22] )
func( **{"a1":11,"a2":22} )
              形参用 *和**，实参也用 *和**
                def func(*args,**kwargs):
print(args,kwargs)
func( 11, 22 )
func( 11, 22, name="武沛齐", age=18 )
# 小坑，([11,22,33], {"k1":1,"k2":2}), {}
func( [11,22,33], {"k1":1,"k2":2} )
# args=(11,22,33),kwargs={"k1":1,"k2":2}
func( *[11,22,33], **{"k1":1,"k2":2} )
# 值得注意：按照这个方式将数据传递给args和kwargs时，数据是会重新拷贝一份的（可理解为内部循环每个元素并设置到args和kwargs中）。
            在使用format字符串格式化时，也可以这样用：
              v1 = "我是{},年龄：{}。".format("武沛齐",18)
v2 = "我是{name},年龄：{age}。".format(name="武沛齐",age=18)
v3 = "我是{},年龄：{}。".format(*["武沛齐",18])
v4 = "我是{name},年龄：{age}。".format(**{"name":"武沛齐","age":18})
        函数和函数名
          函数名其实就是一个变量，这个变量只不过代指的函数而已
          注意：函数必须先定义才能被调用执行（解释型语言）
            # 错误
ret = add(1,2)
print(ret)
def add(n1,n2):
return n1 + n2
          函数做元素 （比如放在列表里面），且由于函数可被哈希，因此也可作集合的元素、字典的键
            def func():
return 123
data_list = ["武沛齐", "func", func , func() ]
print( data_list[0] ) # 字符串"武沛齐"
print( data_list[1] ) # 字符串 "func"
print( data_list[2] ) # 函数 func
print( data_list[3] ) # 整数 123
res = data_list[2]()
print( res ) # 执行函数 func，并获取返回值；print再输出返回值。
print( data_list[2]() ) # 123
          函数名赋值
            将函数名赋值给其他变量，函数名其实就个变量，代指某函数；如果将函数名赋值给另外一个变量，则此变量也会代指该函数
              def func(a1,a2):
print(a1,a2)
xxxxx = func
# 此时，xxxxx和func都代指上面的那个函数，所以都可以被执行。
func(1,1)
xxxxx(2,2)
            对函数名重新赋值，如果将函数名修改为其他值，函数名便不再代指函数
              def func(a1,a2):
print(a1,a2)
# 执行func函数
func(11,22)
# func重新赋值成一个字符串
func = "武沛齐"
print(func) # "武沛齐"
            不要在定义函数时与python内置的函数同名！
          函数名做参数和返回值
            参数
              def plus(num):
return num + 100
def handler(func):
res = func(10) # 110
msg = "执行func，并获取到的结果为:{}".format(res)
print(msg) # 执行func，并获取到的结果为:110
# 执行handler函数，将plus作为参数传递给handler的形式参数func
handler(plus)
            返回值
              def plus(num):
return num + 100
def handler():
print("执行handler函数")
return plus
result = handler()
data = result(20) # 120
print(data)
          返回值和print （要会分析！）
            在函数中使用print，只是用于在某个位置输出内容而已
            在函数中使用return，是为了将函数的执行结果返回给调用者，以便于后续其他操作
              def add(n1,n2):
print(n1 + n2)
v1 = add(1,3)
print(v1)
# 输出
4
None
        函数作用域
          作用域：可以理解为一块空间，这块空间的数据是可以共享的。通俗点来说，作用域就类似于一个房子，房子中的东西归里面的所有人共享，其他房子的人无法获取
          函数为作用域
            Python以函数为作用域，所以在函数内创建的所有数据，可以此函数中被使用，无法在其他函数中被使用
          全局和局部
            Python中以函数为作用域，函数的作用域其实是一个局部作用域
            全局作用域：全局作用域中创建的变量称之为【全局变量】，可以在全局作用域中被使用，也可以在其局部作用域中被使用
            注意：全局变量一般都是大写
            局部作用域： 函数内部维护的就是一个局部作用域，在各自函数内部创建变量称之为【局部变量】，且局部变量只能在此作用域中被使用。局部作用域中想使用某个变量时，寻找的顺序为：《优先在局部作用域中寻找，如果没有则去上级作用域中寻找》
          global关键字
            默认情况下，在局部作用域对全局变量只能进行：读取和修改内部元素（可变类型），无法对全局变量进行重新赋值。
              COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
# 不是对全部变量赋值，而是在局部作用域中又创建了一个局部变量 CITY_LIST 。
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
#输出
['河北', '河南', '山西']
中国
['北京', '上海', '深圳']
            想要修改全局变量，则可基于global关键字进行修改
              COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
global CITY_LIST
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
global COUNTRY
COUNTRY = "中华人民共和国"
print(COUNTRY)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
#输出
['河北', '河南', '山西']
中华人民共和国
中华人民共和国
['河北', '河南', '山西']
          nolocal关键字（少用）
            修改上一作用域的变量
              name = 'root'
def outer():
name = 'alex'
def func():
name = "武沛齐"
def inner():
nonlocal name
name = 123
inner()
print(name) #123
func()
print(name) #alex
outer()
print(name) #root
      函数高级
        函数嵌套
          - 优先在自己的作用域找，自己没有就去上级作用域。
            # 1. 在全局作用域定义了函数func
def func():
print("你好")
# 2. 在全局作用域找到func函数并执行。
func()
# 3.在全局作用域定义了execute函数
def execute():
print("开始")
# 优先在当前函数作用域找func函数，没有则向上级作用域中寻找。
func()
print("结束")
# 4.在全局作用域执行execute函数
execute()
          - 在作用域中寻找值时，要确保此次此刻值是什么。
          - 分析函数的执行，并确定函数`作用域链`。（函数嵌套）
        闭包
          将数据封装在一个包（区域）中，使用时再去里面取。（本质上 闭包是基于函数嵌套搞出来的一个特殊嵌套）
          闭包应用场景1：封装数据防止污染全局
          闭包应用场景2：封装数据封到一个包里，使用时在取
            def task(arg):
def inner():
print(arg)
return inner
inner_func_list = []
for val in [11,22,33]:
inner_func_list.append( task(val) )
inner_func_list[0]() # 11
inner_func_list[1]() # 22
inner_func_list[2]() # 33
        装饰器
          实现效果：可以在不改变原函数内部代码 和 调用方式的前提下，实现在函数执行和执行扩展功能。
          装饰器实现原理
            在运行func函数时会运行@outer，也就是func = outer(func)
            @outer # func = outer(func)
def func():
print("我是func函数")
value = (11, 22, 33, 44)
return value
          基本写法（可拓展性强）
            def outer(origin):
def inner(*args, **kwargs):
# 执行前
res = origin(*args, **kwargs) # 调用原来的func函数
# 执行后
return res
return inner
@outer
def func():
pass
func()
          其他拓展
            print(admin.__name__) # 获取函数名
print(admin.__doc__) # 获取函数的注释
            装饰器的本质：你会发现装饰器实际上就是将原函数更改为其他的函数，然后再此函数中再去调用原函数，所以调用用了装饰器的函数后取获取名字，得到的不在是原函数的名字。
            如果要避免上述情况（以后如果不避免会出很多问题），就在装饰器里面加一个functools：
              import functools
def auth(func):
@functools.wraps(func) # inner.__name__ = func.__name__ inner.__doc__ = func.__doc__
def inner(*args, **kwargs):
res = func(*args, **kwargs) # 执行原函数
return res
return inner
      生成器
        生成器是由函数+yield关键字创造出来的写法，在特定情况下，用他可以帮助我们节省内存。 当函数中有yield存在时，这个函数就是生成器函数
        特点：记录在函数中的执行位置，下次执行next时，会从上一次的位置基础上再继续向下执行
        注意：
          yield后面可以放任意类型的东西
          执行生成器函数func，返回的生成器对象，可以通过循环或者next（）来输出
            循环 （一般这样用）
              def func():
print(123)
print(123)
yield 1234 # 有点像return，执行到这个位置之后，就不再执行。
print(456)
yield 666 # 有点像return，执行到这个位置之后，就不再执行。
print(789)
data = func()
# 基于for循环执行生成器对象
for item in data:
# next(data)
print(item,end=" ")
            next（）
              def func():
print(123)
print(123)
yield 1234 # 有点像return，执行到这个位置之后，就不再执行。
print(456)
yield 666 # 有点像return，执行到这个位置之后，就不再执行。
print(789)
# return None # 程序就会报错，生成器中的代码执行完毕了 StopIteration
# 执行生成器函数时，函数体默认不会被执行；返回的是一个生成器对象。
v1 = func()
print(v1) # <generator object func at 0x7fd0280f75f0>
# next里面放生成器对象，进入生成器函数并执行其中的代码
n1 = next(v1)
print(n1)
# next里面放生成器对象，进入生成器函数并执行其中的代码（从上次yield返回位置继续向下）
n2 = next(v1)
print(n2)
# next里面放生成器对象，进入生成器函数并执行其中的代码（从上次yield返回位置继续向下）
n3 = next(v1) #这一下因为后面没有yield而是return None，所以会报错
          执行生成器函数时，函数内部代码不会执行（省内存）
        拓展：生成器货站 （了解即可）
          def func():
print(111)
v1 = yield 1
print(v1)
print(222)
v2 = yield 2
print(v2)
print(333)
v3 = yield 3
print(v3)
print(444)
data = func()
#.send（）是也是一个触发生成器运行的函数
n1 = data.send(None) #第一次发送的必须是None ，这里n1得到的是 1
print(n1) #1
n2 = data.send(666) #这里发送出去的数据（即666）由 v1 来接受 ，n2接收2
print(n2)
n3 = data.send(777) #下面同上
print(n3)
n4 = data.send(888)
print(n4)
        补充：yield from（少用）
          def foo():
yield 2
yield 2
yield 2
def func():
yield 1
yield 1
yield 1
yield from foo()
yield 1
yield 1
for item in func():
print(item)
#输出
1
1
1
2
2
2
1
1
      匿名函数 - lambda 表达式
        匿名函数，是基于lambda表达式实现定义一个可以没有名字的函数
        函数格式：lambda 参数:函数体
          参数，支持任意参数
            lambda x: 函数体
lambda x1,x2: 函数体
lambda *args, **kwargs: 函数体
          函数体，只能支持单行的代码
            def xxx(x):
return x + 100
lambda x: x + 100
          返回值，默认将函数体单行代码执行的结果返回给函数的执行体
            func = lambda x: x + 100
v1 = func(10)
print(v1) # 110
        拓展：三元运算 （主要写简单的条件语句）
          基本语句： 结果 = 条件成立时 if 条件 else 不成立
          num = input("请写入内容")
data = "臭不要脸" if "苍老师" in num else "正经人"
print(data)
#如果num里面有"苍老师"，就返回 "臭不要脸"，否则返回"正经人"
  模块
    自定义模块
      包和模块
        模块，一个py文件
        包，含多个py文件的文件夹
          在包（文件夹）中有一个默认内容为空的`__init__.py`的文件，一般用于描述当前包的信息（在导入他下面的模块时，也会自动加载） py3可有可无
      导入路径问题
      导入方式
        第一类：import xxxx （开发中，一般多用于导入sys.path目录下的一个py文件）
          模块级别
            ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ ├── tencent
│ │ ├── __init__.py
│ │ ├── sms.py
│ │ └── wechat.py
│ └── utils.py
├── many.py
└── run.py
          包级别
            ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ └── utils.py
├── third
│ ├── __init__.py
│ ├── ali
│ │ └── oss.py
│ └── tencent
│ ├── __init__.py
│ ├── __pycache__
│ ├── sms.py
│ └── wechat.py
└── run.py
        第二类：from xxx import xxx 【常用】，一般适用于多层嵌套和导入模块中某个成员的情况。
          成员（即函数）级别
            ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ └── utils.py
├── many.py
└── run.py
            提示：基于from模式也可以支持 `from many import *`，即：导入一个模块中所有的成员（可能会重名，所以用的少）
          模块级别
            ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ └── utils.py
├── many.py
└── run.py
          包级别
            ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ ├── tencent
│ │ ├── __init__.py
│ │ ├── sms.py
│ │ └── wechat.py
│ └── utils.py
├── many.py
└── run.py
            相对导入只能用在包中的py文件中（即：嵌套在文件中的py文件才可以使用，项目根目录下无法使用）
            这个导入时在__init__.py 里面要先一步进行引用，如上例：from . import sms
from . import wechat
#这两个一定要写，不然出问题。
        第三类：导入别名
          如果项目中导入 成员/模块/包 有重名，那么后导入的会覆盖之前导入，为了避免这种情况的发生，Python支持重命名 （使用起来也会更简单）
          from xxx.xxx import xx as xo
import x1.x2 as pg
      主文件
        其实就是在程序执行的入口文件
        例子
          ├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ ├── tencent
│ │ ├── __init__.py
│ │ ├── sms.py
│ │ └── wechat.py
│ └── utils.py
├── many.py
└── run.py
    第三方模块
      安装方式
        pip
        源码
        wheel
      模块
        requests
        openpyxl
        moviepy
    内置模块
      os
        import os
# 1. 获取当前脚本绝对路径
"""
abs_path = os.path.abspath(__file__)
print(abs_path)
"""
# 2. 获取当前文件的上级目录
"""
base_path = os.path.dirname( os.path.dirname(路径) ）
print(base_path)
"""
# 3. 路径拼接
"""
p1 = os.path.join(base_path, 'xx')
print(p1)
p2 = os.path.join(base_path, 'xx', 'oo', 'a1.png')
print(p2)
"""
# 4. 判断路径是否存在
"""
exists = os.path.exists(p1)
print(exists)
"""
# 5. 创建文件夹
"""
os.makedirs(路径)
"""
"""
path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
if not os.path.exists(path):
os.makedirs(path)
"""
# 6. 是否是文件夹
"""
file_path = os.path.join(base_path, 'xx', 'oo', 'uuuu.png')
is_dir = os.path.isdir(file_path)
print(is_dir) # False
folder_path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
is_dir = os.path.isdir(folder_path)
print(is_dir) # True
"""
# 7. 删除文件或文件夹
"""
os.remove("文件路径")
"""
"""
path = os.path.join(base_path, 'xx')
shutil.rmtree(path)
"""
        listdir，查看目录下所有的文件
import os
print(os.listdir("C:/"))
        walk，查看目录下所有的文件（含子孙文件）
        import os
"""
data = os.listdir("/Users/wupeiqi/PycharmProjects/luffyCourse/day14/commons")
print(data)
# ['convert.py', '__init__.py', 'page.py', '__pycache__', 'utils.py', 'tencent']
"""
"""
要遍历一个文件夹下的所有文件，例如：遍历文件夹下的所有mp4文件
"""
data = os.walk("/Users/wupeiqi/Documents/视频教程/路飞Python/mp4")
for path, folder_list, file_list in data:
for file_name in file_list:
file_abs_path = os.path.join(path, file_name)
ext = file_abs_path.rsplit(".",1)[-1]
if ext == "mp4":
print(file_abs_path)
      sys
        import sys
# 1. 获取解释器版本
"""
print(sys.version)
print(sys.version_info)
print(sys.version_info.major, sys.version_info.minor, sys.version_info.micro)
"""
# 2. 导入模块路径
"""
print(sys.path)
"""
        #argv，执行脚本时，python解释器后面传入的参数
import sys
print(sys.argv)
# [
# '/Users/wupeiqi/PycharmProjects/luffyCourse/day14/2.接受执行脚本的参数.py'
# ]
# [
# "2.接受执行脚本的参数.py"
# ]
# ['2.接受执行脚本的参数.py', '127', '999', '666', 'wupeiqi']
# 例如，请实现下载图片的一个工具。
def download_image(url):
print("下载图片", url)
def run():
# 接受用户传入的参数
url_list = sys.argv[1:]
for url in url_list:
download_image(url)
if __name__ == '__main__':
run()
      shutil
        import shutil
# 1. 删除文件夹
"""
path = os.path.join(base_path, 'xx')
shutil.rmtree(path)
"""
# 2. 拷贝文件夹
"""
shutil.copytree("/Users/wupeiqi/Desktop/图/csdn/","/Users/wupeiqi/PycharmProjects/CodeRepository/files")
"""
# 3.拷贝文件
"""
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/")
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/x.png")
"""
# 4.文件或文件夹重命名
"""
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/xxxx.png")
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/files","/Users/wupeiqi/PycharmProjects/CodeRepository/images")
"""
# 5. 压缩文件
"""
# base_name，压缩后的压缩包文件
# format，压缩的格式，例如："zip", "tar", "gztar", "bztar", or "xztar".
# root_dir，要压缩的文件夹路径
"""
# shutil.make_archive(base_name=r'datafile',format='zip',root_dir=r'files')
# 6. 解压文件
"""
# filename，要解压的压缩包文件
# extract_dir，解压的路径
# format，压缩文件格式
"""
# shutil.unpack_archive(filename=r'datafile.zip', extract_dir=r'xxxxxx/xo', format='zip')
      random
        import random
# 1. 获取范围内的随机整数
v = random.randint(10, 20)
print(v)
# 2. 获取范围内的随机小数
v = random.uniform(1, 10)
print(v)
# 3. 随机抽取一个元素
v = random.choice([11, 22, 33, 44, 55])
print(v)
# 4. 随机抽取多个元素
v = random.sample([11, 22, 33, 44, 55], 3)
print(v)
# 5. 打乱顺序
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
random.shuffle(data)
print(data)
      xml
        见xml（左边·）
      configparser
        见ini
      time
        time.time()
          获取当前时间戳（自1970-1-1 00:00）
        time.timezone
          时区(中国是东八区)
        time.sleep()
          停止n秒，再执行后续的代码。
          time.sleep(5)
      datetime
        from datetime import datetime, timezone, timedelta （导入方式）
        datatime
          注意
            datetime之间相减，计算间隔时间（不能相加）
              v1 = datetime.now()
v2 = datetime.utcnow() # 当前UTC时间
data = v1 - v2
print(data.days, data.seconds, data.microseconds)
            datetime可以进行比较
            时间的加减 datetime类型 ± timedelta类型
              v1 = datetime.now() v2 = v1 + timedelta(days=140, minutes=5)
print(v2)
          用法
            获取本地时间
              v1 = datetime.now() # 当前本地时间
print(v1)
            获取其他时区时间
              tz = timezone(timedelta(hours=7)) # 当前东7区时间
v2 = datetime.now(tz)
print(v2)
              注意：东时区为正，西时区为负，计算公式为“计算的区时=已知区时－（已知区时的时区-要计算区时的时区）” 想要哪个时区，就输在” hours= “的右边即可
            获取当前utc时间
              v3 = datetime.utcnow() # 当前UTC时间
print(v3)
            与字符串之间的转换
              字符串格式的时间 ---> 转换为datetime格式时间 strptime
                text = "2021-11-11"
v1 = datetime.strptime(text,'%Y-%m-%d') # %Y 年，%m，月份，%d，天。
print(v1)
              datetime格式 ----> 转换为字符串格式strftime
                v1 = datetime.now()
val = v1.strftime("%Y-%m-%d %H:%M:%S")
print(val)
            与时间戳之间的转化
              时间戳格式 --> 转换为datetime格式fromtimestamp
                ctime = time.time() # 11213245345.123
v1 = datetime.fromtimestamp(ctime)
print(v1)
              datetime格式 ---> 转换为时间戳格式timestamp
                v1 = datetime.now()
val = v1.timestamp()
print(val)
        timezone（在上面有用到）
        timedelta（在上面有用到）
      re
        对文本处理的一些函数
          findall
            获取匹配到的所有数据
            （例子都是用这个写的，这里就不加示例了）
          match
            从起始位置开始匹配，匹配成功返回一个对象，未匹配成功返回None
            例子
              text = "大小逗2B最逗3B欢乐"
data = re.match("逗\dB", text)
print(data) # None
              text = "逗2B最逗3B欢乐"
data = re.match("逗\dB", text)
if data:
content = data.group() # "逗2B"
print(content)
          search
            浏览整个字符串去匹配第一个，未匹配成功返回None
            例子
              text = "大小逗2B最逗3B欢乐"
data = re.search("逗\dB", text)
if data:
print(data.group()) # "逗2B"
          sub
            替换匹配成功的位置
            例子
              text = "逗2B最逗3B欢乐"
data = re.sub("\dB", "沙雕", text)
print(data) # 逗沙雕最逗沙雕欢乐
              text = "逗2B最逗3B欢乐"
data = re.sub("\dB", "沙雕", text, 1)
print(data) # 逗沙雕最逗3B欢乐
          split
            根据匹配成功的位置分割
            例子
              text = "逗2B最逗3B欢乐"
data = re.split("\dB", text)
print(data) # ['逗', '最逗', '欢乐']
              text = "逗2B最逗3B欢乐"
data = re.split("\dB", text, 1)
print(data) # ['逗', '最逗3B欢乐']
          finditer
            返回的是一个迭代器，也就是说这样一执行并不会立即在内存中把所有数据找到，而是需要通过在后面循环的时候，每循环一次找一次，比较省内存。
            例子
              未循环是这样的
                text = "逗2B最逗3B欢乐"
data = re.finditer("逗\dB",text)
print(data) #<callable_iterator object at 0x000001989E5791C0>
              循环是这样的
                text = "逗2B最逗3B欢乐"
data = re.finditer("\dB", text)
for item in data:
print(item.group(),end=" ") #2B 3B
              可以将取出的数据进行命名，返回字典（这里以获取身份证上的出生日期为例）
                基本格式：（？P<取的名字>要获取的数据） 其中（？P<>）是固定的语法
                text = "dsf130429191912015219k13042919591219521Xkk"
data_list = re.finditer("\d{6}(?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2})\d{3}[\d|X]", text)
for item in data_list:
info_dict = item.groupdict()
print(info_dict)
# {'year': '1919', 'month': '12', 'day': '01'}
# {'year': '1959', 'month': '12', 'day': '19'}
        方法
          字符相关
            ”wupeiqi“ 匹配文本中的wupeiqi
              常用来统计字符串中某个字符出现的次数
              text = "你好wupeiqi,阿斯顿发wupeiqasd 阿士大夫能接受的wupeiqiff"
data_list = re.findall("wupeiqi", text)
print(data_list) # ['wupeiqi', 'wupeiqi'] 可用于计算字符串中某个字符出现的次数
            [abc] 匹配a或b或c 字符
              text = "你2b好wupeiqi,阿斯顿发awupeiqasd 阿士大夫a能接受的wffbbupqaceiqiff"
data_list = re.findall("[abc]", text)
print(data_list) # ['b', 'a', 'a', 'a', 'b', 'b', 'c']
            [^abc] 匹配除了abc意外的其他字符
              text = "你wffbbupceiqiff"
data_list = re.findall("[^abc]", text)
print(data_list) # ['你', 'w', 'f', 'f', 'u', 'p', 'e', 'i', 'q', 'i', 'f', 'f']
            [a-z] 匹配a~z的任意字符（ [0-9]也可以 ）
              text = "alexrootrootadmin"
data_list = re.findall("t[a-z]", text)
print(data_list) # ['tr', 'ta']
            . 代指除换行符以外的任意字符
              text = "alexraotrootadmin"
data_list = re.findall("r.o", text)
print(data_list) # ['rao', 'roo']
            \w 代指字母或数字或下划线（汉字）
              text = "北京武沛alex齐北 京武沛alex齐"
data_list = re.findall("武\w+x", text)
print(data_list) # ['武沛alex', '武沛alex']
            \d 代指数字
              ext = "root-ad32min-add3-admd1in"
data_list = re.findall("d\d", text)
print(data_list) # ['d3', 'd3', 'd1']
            \s 代指任意的空白符，包括空格、制表符等
              text = "root admin add admin"
data_list = re.findall("a\w+\s\w+", text)
print(data_list) # ['admin add']
          数量相关
            * 重复0次或更多次
              text = "他是大B个，确实是个大2B。"
data_list = re.findall("大2*B", text)
print(data_list) # ['大B', '大2B']
            + 重复1次或更多次
              text = "他是大B个，确实是个大2B，大3B，大66666B。"
data_list = re.findall("大\d+B", text)
print(data_list) # ['大2B', '大3B', '大66666B']
            ? 重复0次或1次
              text = "他是大B个，确实是个大2B，大3B，大66666B。"
data_list = re.findall("大\d?B", text)
print(data_list) # ['大B', '大2B', '大3B']
            {n} 重复n次
              text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("151312\d{5}", text)
print(data_list) # ['15131255789']
            {n，} 重复n次或更多次
              text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("\d{9,}", text)
print(data_list) # ['442662578', '15131255789']
            {n,m} 重复n到m次 （头尾都可以取到）
              text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("\d{10,15}", text)
print(data_list) # ['15131255789']
          括号（分组）
            提取数据区域
              例子一
                text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("15131(2\d{5})", text)
print(data_list) # ['255789']
              例子二
                text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来15131266666呀"
data_list = re.findall("15(13)1(2\d{5})", text)
print(data_list) # [ ('13', '255789') ]
              例子三
                text = "楼主太牛逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("(15131(2\d{5}))", text)
print(data_list) # [('15131255789', '255789')]
            获取指定区域 + 或条件 （取“或”时，用|来取）
              例子一
                text = "楼主15131root太牛15131alex逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("15131(2\d{5}|r\w+太)", text)
print(data_list) # ['root太', '255789']
              例子二
                text = "楼主15131root太牛15131alex逼了，在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主，手机号也可15131255789，搞起来呀"
data_list = re.findall("(15131(2\d{5}|r\w+太))", text)
print(data_list) # [('15131root太', 'root太'), ('15131255789', '255789')]
          起始和结束
            要求用户输入的内容必须是指定的内容开头和结尾，比就需要用到如下两个字符
              `^` 开始 （从开头就开始检索）
                text = "啊442662578@qq.com我靠"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # []
                text = "442662578@qq.com"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # ['442662578@qq.com']
              `$` 结束 （结尾必须是$前的东西，不能是别的）
          特殊字符
            转义
              由于正则表达式中 `* . \ { } ( ) ` 等都具有特殊的含义，所以如果想要在正则中匹配这种指定的字符，需要转义
              text = "我是你{5}爸爸"
data = re.findall("你\{5\}爸", text)
print(data)
          贪婪匹配与非贪婪匹配
            贪婪匹配（能有多长就多长）
              text = "alexraotrootadmin"
data_list = re.findall("r.+o", text) # 贪婪匹配
print(data_list) # ['raotroo']
            非贪婪匹配（一旦遇到合适的就会停止）（有个？）
              text = "alexraotrootadmin"
data_list = re.findall("r.+?o", text) # 非贪婪匹配
print(data_list) # ['rao']
      json
        json格式，是一个数据格式（本质上就是个字符串，常用语网络数据传输）
        用法
          dumps
            数据类型 -> json ，一般称为：序列化
            data = [{"id": 1, "name": "武沛齐", "age": 18},{"id": 2, "name": "alex", "age": 18}]
            res = json.dumps(data, ensure_ascii=False)
(其中ensure_ascii是要不要将文字转码为ascii的选项，不转则方便阅读)
            print(res) # '[{"id": 1, "name": "武沛齐", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
          loads
            json格式 -> 数据类型，一般称为：反序列化
            data_string = '[{"id": 1, "name": "武沛齐", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
data_list = json.loads(data_string)
print(data_list) #[{'id': 1, 'name': '武沛齐', 'age': 18}, {'id': 2, 'name': 'alex', 'age': 18}]
          dump(不常用)
            将数据序列化并写入文件
            data = [
{"id": 1, "name": "武沛齐", "age": 18},
{"id": 2, "name": "alex", "age": 18},
]
file_object = open('xxx.json', mode='w', encoding='utf-8')
json.dump(data, file_object) （前面填需要解析的数据，后面填保存的地方）
file_object.close()
          load（不常用）
            读取文件中的数据并反序列化为python的数据类型
            file_object = open('xxx.json', mode='r', encoding='utf-8')
data = json.load(file_object)
print(data)
file_object.close()
        转换的类型要求
          Python
            JSON
          dict
            object
          list，tuple
            array（数组）
          str
            string
          int,float
            number
          True
            true
          False
            false
          None
            null
      hashlib
        import hashlib
hash_object = hashlib.md5()
hash_object.update("武沛齐".encode('utf-8'))
result = hash_object.hexdigest()
print(result)
        import hashlib
hash_object = hashlib.md5("iajfsdunjaksdjfasdfasdf".encode('utf-8'))
hash_object.update("武沛齐".encode('utf-8'))
result = hash_object.hexdigest()
print(result)
    开发规范
      目录结构
        crm
├── app.py 文件，程序的主文件（尽量精简）
├── config.py 文件，配置文件（放相关配置信息，代码中读取配置信息，如果想要修改配置，即可以在此修改，不用再去代码中逐一修改了）
├── db 文件夹，存放数据
├── files 文件夹，存放文件
├── src 包，业务处理的代码
└── utils 包，公共功能
      单文件应用
      单可执行文件
      多可执行文件
    深浅拷贝
      浅拷贝
        不可变类型，不拷贝
          import copy
v1 = "武沛齐"
print(id(v1)) # 140652260947312
v2 = copy.copy(v1)
print(id(v2)) # 140652260947312
          按理说拷贝v1之后，v2的内存地址应该不同，但由于python内部优化机制，内存地址是相同的，因为对不可变类型而言，如果以后修改值，会重新创建一份数据，不会影响原数据，所以，不拷贝也无妨
        可变类型，只拷贝第一层
          import copy
v1 = ["武沛齐", "root", [44, 55]]
print(id(v1)) # 140405837216896
print(id(v1[2])) # 140405837214592
v2 = copy.copy(v1)
print(id(v2)) # 140405837214784
print(id(v2[2])) # 140405837214592
      深拷贝
        不可变类型，不拷贝
          import copy
v1 = "武沛齐"
print(id(v1)) # 140188538697072
v2 = copy.deepcopy(v1)
print(id(v2)) # 140188538697072
        特殊的元组：
          元组元素中无可变类型，不拷贝
            import copy
v1 = ("武沛齐", "root")
print(id(v1)) # 140243298961984
v2 = copy.deepcopy(v1)
print(id(v2)) # 140243298961984
          元素元素中有可变类型，找到所有【可变类型】或【含有可变类型的元组】 均拷贝一份
            import copy
v1 = ("武沛齐", "root", [11, [44, 55], (11, 22), (11, [], 22), 33])
v2 = copy.deepcopy(v1)
print(id(v1)) # 140391475456384
print(id(v2)) # 140391475456640
print(id(v1[2])) # 140352552779008
print(id(v2[2])) # 140352552920448
print(id(v1[2][1])) # 140642999940480
print(id(v2[2][1])) # 140643000088832
print(id(v1[2][2])) # 140467039914560
print(id(v2[2][2])) # 140467039914560
print(id(v1[2][3])) # 140675479841152
print(id(v2[2][3])) # 140675480454784
        可变类型，找到所有层级的 【可变类型】或【含有可变类型的元组】 均拷贝一份
          import copy
v1 = ["武沛齐", "root", [11, [44, 55], (11, 22), (11, [], 22), 33]]
v2 = copy.deepcopy(v1)
print(id(v1)) # 140391475456384
print(id(v2)) # 140391475456640
print(id(v1[2])) # 140352552779008
print(id(v2[2])) # 140352552920448
print(id(v1[2][1])) # 140642999940480
print(id(v2[2][1])) # 140643000088832
print(id(v1[2][2])) # 140467039914560
print(id(v2[2][2])) # 140467039914560
print(id(v1[2][3])) # 140675479841152
print(id(v2[2][3])) # 140675480454784
  文件操作相关
    回顾编码相关的知识
      字符串类型（str）
        在程序中用于表示文字信息，本质上是unicode编码中的二进制
      字节类型（bytes）
        可表示文字信息，本质上是utf-8/gbk等编码的二进制（对unicode进行压缩，方便文件存储和网络传输。）
          name = "武沛齐"
data = name.encode('utf-8')
print(data) # b'\xe6\xad\xa6\xe6\xb2\x9b\xe9\xbd\x90'
result = data.decode('utf-8')
print(result) # "武沛齐"
        可表示原始二进制（图片、文件等信息）
    读文件
      读文本文件
        路径
          绝对路径：'/Users/wupeiqi/PycharmProjects/luffyCourse/day09/info.txt'
          相对路径：'info.txt'
        需要手动解码和编码的方法
          # 1.打开文件
file_object = open('info.txt', mode='rb')
# 2.读取文件内容，并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()
print(data) # b'alex-123\n\xe6\xad\xa6\xe6\xb2\x9b\xe9\xbd\x90-123'
text = data.decode("utf-8")
print(text)
        不需要手动解码和编码的方法
          # 1.打开文件
file_object = open('info.txt', mode='rt', encoding='utf-8')
# 2.读取文件内容，并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()
print(data)
          其中encoding如果不指定的话是默认“gbk”解码的模式
      读图片等非文本内容文件
        file_object = open('a1.png', mode='rb')
data = file_object.read()
file_object.close()
print(data) # \x91\xf6\xf2\x83\x8aQFfv\x8b7\xcc\xed\xc3}\x7fT\x9d{.3.\xf1{\xe8\...
      读文件的路径问题
        绝对路径（如果文件名中有\n \t，容易出问题，程序分不清）
          # 1.打开文件
file_object = open('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/info.txt', mode='rt', encoding='utf-8')
# 2.读取文件内容，并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()
          避免出问题的两种写法
            用\\代替\ (避免文件名中有\) 例子：'C:\\new\\info.txt'
            在“”的前面加r (避免文件路径中不相信组合出\n \t等有特殊含义的字符) 例子：r'C:\new\info.txt'
        相对路径
          在pycharm中执行没问题，但在cmd中执行会有问题，因为在pycharm中执行时会自动加入前面的路径，可用os模块中的东西解决，后面有写！
        文件不存在时的报错
          用os里面的os.path.exists()来看是否存在
            # 判断路径是否存在？
import os
file_path = "/Users/wupeiqi/PycharmProjects/luffyCourse/day09/info.txt"
exists = os.path.exists(file_path)
if exists:
# 1.打开文件
file_object = open('infower.txt', mode='rt', encoding='utf-8')
# 2.读取文件内容，并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()
print(data)
else:
print("文件不存在")
    写文件
      写文本文件（两种方法，和前面的读文件相呼应）
        # 1.打开文件
# 路径：t1.txt
# 模式：wb（要求写入的内容需要是字节类型）
file_object = open("t1.txt", mode='wb')
# 2.写入内容
file_object.write( "武沛齐".encode("utf-8") )
# 3.文件关闭
file_object.close()
        file_object = open("t1.txt", mode='wt', encoding='utf-8')
file_object.write("武沛齐")
file_object.close()
      写图片等文件
        f1 = open('a1.png',mode='rb')
content = f1.read()
f1.close()
f2 = open('a2.png',mode='wb')
f2.write(content)
f2.close() #相当于复制了一次a1图片
      补充：从网络上爬下来的数据的三种解析方式
        文本：res.text
        json: res.json
        音视频和图片：res.content
    文件打开模式
      总表
        ========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
The default mode is 'rt' (open for reading text).
      文件常见的打开方式
        只能读或写
          只读：`r`、`rt`、`rb` （常用）
- 存在，读
- 不存在，报错
          只写：`w`、`wt`、`wb`（常用）
- 存在，清空再写
- 不存在，创建再写
          只写：`a`、`at`、`ab`【尾部追加】（常用）
- 存在，尾部追加。
- 不存在，创建再写。
          只写：`x`、`xt`、`xb`（不常用）
- 存在，报错
- 不存在，创建再写。
        读写
          r类
            r+、rt+、rb+，默认光标位置：起始位置 （若有东西，写入的东西会从头开始替换，一个一个的替换）
            file_object = open('info.txt', mode='rt+')
# 读取内容
data = file_object.read()
print(data)
# 写入内容
file_object.write("你好呀")
file_object.close()
            file_object = open('info.txt', mode='rt+')
# 写入内容
file_object.write("alex") #此时光标在alex的后面，读文件时从alex后面开始读
# 读取内容
data = file_object.read()
print(data) # -123
file_object.close()
          w类
            w+、wt+、wb+，默认光标位置：起始位置（清空文件）
            file_object = open('info.txt', mode='wt+')
# 读取内容
data = file_object.read()
print(data) #None
# 写入内容
file_object.write("你好呀")
# 将光标位置重置起始
file_object.seek(0)
# 读取内容
data = file_object.read()
print(data) #你好呀
file_object.close()
          x类（不常用）
            x+、xt+、xb+，默认光标位置：起始位置（新文件）
          a类
            a+、at+、ab+，默认光标位置：末尾
            file_object = open('info.txt', mode='at+')
# 写入内容
file_object.write("武沛齐")
# 将光标位置重置起始
file_object.seek(0)
# 读取内容
data = file_object.read()
print(data)
file_object.close()
    常见功能
      read，读
        读所有【常用】
          f = open('info.txt', mode='r',encoding='utf-8')
data = f.read()
f.close() （当mode=“r”时，后面默认会有个t，也就是rt）
        读n个字符（字节）【会用到】
          f = open('info.txt', mode='rt', encoding='utf-8')
# 读1个字符
data = f.read(1) #因为上面是 mode='rt'，所以读字符
f.close()
print(data) # 武
          f = open('info.txt', mode='rb')
# 读1个字节
data = f.read(3) #因为上面mode='rb'，所以读字节
f.close() #注意：如果字节读到了文字（3个字节）里
面，后写入东西，易报错（其实基本都会报
错），因为系统中字节的组合变了。
#还有，读几个字节光标就是在第几个字节的
后面出现。
print(data, type(data)) # b'\xe6\xad\xa6' <class 'bytes'>
      readline，读一行
        f = open('info.txt', mode='r', encoding='utf-8')
v1 = f.readline()
print(v1)
v2 = f.readline()
print(v2)
f.close()
        f = open('info.txt', mode='r', encoding='utf-8')
v1 = f.readline()
print(v1)
f.close()
f = open('info.txt', mode='r', encoding='utf-8')
v2 = f.readline()
print(v2)
f.close()
        注意：上面两种的区别，打开后再开启光标重新出现在开头，所以第二个里面的v1 = v2
      循环，读大文件（readline加强版）【常见】
        f = open('info.txt', mode='r', encoding='utf-8')
for line in f:
print(line.strip())
f.close()
      write，写
        f = open('info.txt', mode='a',encoding='utf-8')
f.write("武沛齐")
f.close()
        f = open('info.txt', mode='ab')
f.write( "武沛齐".encode("utf-8") )
f.close()
      flush，刷到硬盘
        f = open('info.txt', mode='a',encoding='utf-8')
while True:
# 不是写到了硬盘，而是写在缓冲区，系统会将缓冲区的内容刷到硬盘。
f.write("武沛齐")
f.flush()
f.close()
        常用这个可以节省内存，而且后面一定要注意是在内存中进行修改，还是在硬盘中进行修改，有时候内存中的东西改了，硬盘中的还没改，有可能会造成困惑
      seek，移动光标位置（字节）
        f = open('info.txt', mode='r+', encoding='utf-8')
# 移动到指定字节的位置
f.seek(3) #如何判断光标seek（3）是什么意思 》》意思是光标前面有几个字节，就代表这个光标在第几个字节
f.write("武沛齐") #比如这个seek（3）就代表光标刚好在武的后面，沛的前面
#如果seek（）到了文字的3个字节的中间，那么一般情况下会出现乱码
#print("a程".encode("utf-8")) #b'a\xe7\xa8\x8b' 字母不会再次编码
f.close()
        注意：在a模式下，调用write在文件中写入内容时，永远只能将内容写入到尾部，不会写到光标的位置。
      tell，获取当前光标位置
        f = open('info.txt', mode='r', encoding='utf-8')
p1 = f.tell()
print(p1) # 0
f.read(3) # 读3个字符 3*3=9字节
p2 = f.tell()
print(p2) # 9
f.close()
        f = open('info.txt', mode='rb')
p1 = f.tell()
print(p1) # 0
f.read(3) # 读3个字节
p2 = f.tell()
print(p2) # 3
f.close()
      上下文管理（常用###），不需要手动关闭文件
        with open("xxxx.txt", mode='rb') as file_object:
data = file_object.read()
print(data)
        在Python 2.7 后，with又支持同时对多个文件的上下文进行管理
          with open("xxxx.txt", mode='rb') as f1, open("xxxx.txt", mode='rb') as f2:
pass
  csv格式文件
    定义
      **逗号分隔值**（Comma-Separated Values，**CSV**，有时也称为**字符分隔值**，因为分隔字符也可以不是逗号），其文件以纯文本形式存储表格数据（数字和文本）。
    例子
      对于这种格式的数据，我们需要利用open函数来读取文件并根据逗号分隔的特点来进行处理。
      股票代码,股票名称,当前价,涨跌额,涨跌幅,年初至今
SH601778,N晶科,6.29,+1.92,-43.94%,+43.94%
SH688566,吉贝尔,52.66,+6.96,+15.23%,+122.29%
...
  ini格式文件
    定义
      ini文件是Initialization File的缩写，平时用于存储软件的的配置文件
    样例
      [mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-bin=py-mysql-bin
character-set-server=utf8
collation-server=utf8_general_ci
log-error=/var/log/mysqld.log
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
[client]
default-character-set=utf8
    操作 （需要用到configparser）
      在前面要有的规定写法
        import configparser
config = configparser.ConfigParser() #这一行就是个固定写法
config.read('files/my.ini', encoding='utf-8') #打开.ini文件
# config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.ini', encoding='utf-8')
      读取所有节点 sections
        ·import configparser
config = configparser.ConfigParser()
config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8')
# config.read('my.conf', encoding='utf-8')
ret = config.sections()
print(ret)
>>输出
['mysqld', 'mysqld_safe', 'client'] #返回一个列表
      读取节点下的键值 items
        import configparser
config = configparser.ConfigParser()
config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8')
# config.read('my.conf', encoding='utf-8')
item_list = config.items("mysqld_safe")
print(item_list)
>>输出
[('log-error', '/var/log/mariadb/mariadb.log'), ('pid-file', '/var/run/mariadb/mariadb.pid')] #返回列表里面套元组
      读取节点下值（根据 节点+键 ） get
        import configparser
config = configparser.ConfigParser()
config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8')
value = config.get('mysqld', 'log-bin')
print(value)
>>输出
py-mysql-bin #返回一个字符串
      检查、删除、添加节点 （依次为 has_section,add_section,remove_section）
        import configparser
config = configparser.ConfigParser()
config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8')
# config.read('my.conf', encoding='utf-8')
# 检查
has_sec = config.has_section('mysqld')
print(has_sec) #True
# 添加节点
config.add_section("SEC_1")
# 节点中设置键值
config.set('SEC_1', 'k10', "123") #3个东西依次为 节点 键 值
config.set('SEC_1', 'name', "哈哈哈哈哈")
config.add_section("SEC_2")
config.set('SEC_2', 'k10', "123")
# 内容写入新文件
config.write(open('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/xxoo.conf', 'w'))
# 删除节点
config.remove_section("SEC_2")
# 删除节点中的键值
config.remove_option('SEC_1', 'k10')
config.write(open('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/new.conf', 'w'))
  XML格式文件（了解即可）
    定义
      [可扩展标记语言](https://baike.baidu.com/item/可扩展标记语言/2885849)，是一种简单的数据存储语言，XML 被设计用来传输和存储数据。
      - 存储，可用来存放配置文件，例如：java的配置文件。
      - 传输，网络传输时以这种格式存在，例如：早期ajax传输的数据、soap协议等。
    样例
      <data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2026</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
    操作（需要导入东西，看后面代码示例！）
      读取文件和内容
        from xml.etree import ElementTree as ET
# ET去打开xml文件 (XML在文件里面用这个框里面的方法)
tree = ET.parse("files/xo.xml")
# 获取根标签
root = tree.getroot()
print(root) # <Element 'data' at 0x7f94e02763b0>
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content) #获取所有的根标签，需要通过循环一层层获取
print(root) # <Element 'data' at 0x7fdaa019cea0>
      读取节点数据
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein" id="999" >
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
# 获取根标签 data
root = ET.XML(content)
country_object = root.find("country")
print(country_object.tag, country_object.attrib)
gdppc_object = country_object.find("gdppc")
print(gdppc_object.tag,gdppc_object.attrib,gdppc_object.text)
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
# 获取根标签 data
root = ET.XML(content)
# 获取data标签的孩子标签
for child in root:
# child.tag = conntry
# child.attrib = {"name":"Liechtenstein"}
print(child.tag, child.attrib)
for node in child:
print(node.tag, node.attrib, node.text)
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
for child in root.iter('year'):
print(child.tag, child.text)
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
v1 = root.findall('country')
print(v1)
v2 = root.find('country').find('rank')
print(v2.text)
      修改和删除节点
        from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
# 修改节点内容和属性
rank = root.find('country').find('rank')
print(rank.text)
rank.text = "999"
rank.set('update', '2020-11-11') #加标签
print(rank.text, rank.attrib)
############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("new.xml", encoding='utf-8')
# 删除节点
root.remove( root.find('country') )
print(root.findall('country'))
############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("newnew.xml", encoding='utf-8')
    构建文档
      创建方式一
        <home> （创建出来的样子）
<son name="儿1">
<grandson name="儿11"></grandson>
<grandson name="儿12"></grandson>
</son>
<son name="儿2"></son>
</home>
        from xml.etree import ElementTree as ET
#先创建标签，再建立练习
# 创建根标签
root = ET.Element("home")
# 创建节点大儿子
son1 = ET.Element('son', {'name': '儿1'})
# 创建小儿子
son2 = ET.Element('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson2 = ET.Element('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son2)
tree = ET.ElementTree(root)
tree.write('oooo.xml', encoding='utf-8', short_empty_elements=False)
#如果没写short_empty_elements=False，则默认为True
      创建方式二
        <famliy> （创建出来的样子）
<son name="儿1">
<grandson name="儿11"></grandson>
<grandson name="儿12"></grandson>
</son>
<son name="儿2"></son>
</famliy>
        from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建大儿子
son1 = root.makeelement('son', {'name': '儿1'})
# 创建小儿子
son2 = root.makeelement('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son2)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8')
      创建方式三
        <famliy>
<son name="儿1">
<age name="儿11">孙子</age>
</son>
<son name="儿2"></son>
</famliy>
        from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建节点大儿子
son1 = ET.SubElement(root, "son", attrib={'name': '儿1'})
# 创建小儿子
son2 = ET.SubElement(root, "son", attrib={"name": "儿2"})
# 在大儿子中创建一个孙子
grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'})
grandson1.text = '孙子'
et = ET.ElementTree(root) #生成文档对象
et.write("test.xml", encoding="utf-8")
      创建方式四
        <user><![CDATA[你好呀]]</user>
        from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("user")
root.text = "<![CDATA[你好呀]]"
et = ET.ElementTree(root) # 生成文档对象
et.write("test.xml", encoding="utf-8")
  Excel格式文件
    需要安装模块 openpyxl
    操作
      读Excel
        读sheet
          from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
# sheet相关操作
# 1.获取excel文件中的所有sheet名称
"""
print(wb.sheetnames) # ['数据导出', '用户列表', 'Sheet1', 'Sheet2']
"""
# 2.选择sheet，基于sheet名称
sheet = wb["数据导出"]
cell = sheet.cell(1, 2)
print(cell.value)
"""
# 3.选择sheet，基于索引位置
"""
sheet = wb.worksheets[0]
cell = sheet.cell(1,2)
print(cell.value)
"""
# 4.循环所有的sheet
"""
for name in wb.sheetnames:
sheet = wb[name]
cell = sheet.cell(1, 1)
print(cell.value)
"""
"""
for sheet in wb.worksheets:
cell = sheet.cell(1, 1)
print(cell.value)
"""
"""
for sheet in wb:
cell = sheet.cell(1, 1)
print(cell.value)
"""
        读sheet中单元格的数据
          from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
sheet = wb.worksheets[0]
# 1.获取第N行第N列的单元格(位置是从1开始）
"""
cell = sheet.cell(1, 1)
print(cell.value)
print(cell.style)
print(cell.font)
print(cell.alignment)
"""
# 2.获取某个单元格
"""
c1 = sheet["A2"]
print(c1.value)
c2 = sheet['D4']
print(c2.value)
"""
# 3.第N行所有的单元格
"""
for cell in sheet[1]:
print(cell.value)
"""
# 4.所有行的数据（获取某一行数据）
"""
for row in sheet.rows:
print(row[0].value, row[1].value)
"""
# 5.获取所有列的数据
"""
for col in sheet.columns:
print(col[1].value)
"""
        读合并的单元格
          from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
sheet = wb.worksheets[2]
# 获取第N行第N列的单元格(位置是从1开始）
c1 = sheet.cell(1, 1)
print(c1) # <Cell 'Sheet1'.A1>
print(c1.value) # 用户信息
c2 = sheet.cell(1, 2)
print(c2) # <MergedCell 'Sheet1'.B1>
print(c2.value) # None
          （分隔）
          （分隔）
          from openpyxl import load_workbook
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[2]
for row in sheet.rows:
print(row)
          >>> 输出结果
(<Cell 'Sheet1'.A1>, <MergedCell 'Sheet1'.B1>, <Cell 'Sheet1'.C1>)
(<Cell 'Sheet1'.A2>, <Cell 'Sheet1'.B2>, <Cell 'Sheet1'.C2>)
(<Cell 'Sheet1'.A3>, <Cell 'Sheet1'.B3>, <Cell 'Sheet1'.C3>)
(<MergedCell 'Sheet1'.A4>, <Cell 'Sheet1'.B4>, <Cell 'Sheet1'.C4>) #这一行的mergecell就代表合并的单元格
(<Cell 'Sheet1'.A5>, <Cell 'Sheet1'.B5>, <Cell 'Sheet1'.C5>)
      写Excel
        原Excel文件基础上写内容
          from openpyxl import load_workbook
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[0]
# 找到单元格，并修改单元格的内容
cell = sheet.cell(1, 1)
cell.value = "新的开始"
# 将excel文件保存到p2.xlsx文件中
wb.save("files/p2.xlsx")
        新创建Excel文件写内容
          from openpyxl import workbook
# 创建excel且默认会创建一个sheet（名称为Sheet）
wb = workbook.Workbook()
sheet = wb.worksheets[0] # 或 sheet = wb["Sheet"]
# 找到单元格，并修改单元格的内容
cell = sheet.cell(1, 1)
cell.value = "新的开始"
# 将excel文件保存到p2.xlsx文件中
wb.save("files/p2.xlsx")
      一些更深入的操作（要用的时候查）
        from openpyxl import workbook
wb = workbook.Workbook() # Sheet
# 1. 修改sheet名称
"""
sheet = wb.worksheets[0]
sheet.title = "数据集"
wb.save("p2.xlsx")
"""
# 2. 创建sheet并设置sheet颜色
"""
sheet = wb.create_sheet("工作计划", 0)
sheet.sheet_properties.tabColor = "1072BA"
wb.save("p2.xlsx")
"""
# 3. 默认打开的sheet
"""
wb.active = 0
wb.save("p2.xlsx")
"""
# 4. 拷贝sheet
"""
sheet = wb.create_sheet("工作计划")
sheet.sheet_properties.tabColor = "1072BA"
new_sheet = wb.copy_worksheet(wb["Sheet"])
new_sheet.title = "新的计划"
wb.save("p2.xlsx")
"""
# 5.删除sheet
"""
del wb["用户列表"]
wb.save('files/p2.xlsx')
"""
        from openpyxl import load_workbook
from openpyxl.styles import Alignment, Border, Side, Font, PatternFill, GradientFill
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[1]
# 1. 获取某个单元格，修改值
"""
cell = sheet.cell(1, 1)
cell.value = "开始"
wb.save("p2.xlsx")
"""
# 2. 获取某个单元格，修改值
"""
sheet["B3"] = "Alex"
wb.save("p2.xlsx")
"""
# 3. 获取某些单元格，修改值
"""
cell_list = sheet["B2":"C3"]
for row in cell_list:
for cell in row:
cell.value = "新的值"
wb.save("p2.xlsx")
"""
# 4. 对齐方式
"""
cell = sheet.cell(1, 1)
# horizontal，水平方向对齐方式："general", "left", "center", "right", "fill", "justify", "centerContinuous", "distributed"
# vertical，垂直方向对齐方式："top", "center", "bottom", "justify", "distributed"
# text_rotation，旋转角度。
# wrap_text，是否自动换行。
cell.alignment = Alignment(horizontal='center', vertical='distributed', text_rotation=45, wrap_text=True)
wb.save("p2.xlsx")
"""
# 5. 边框
# side的style有如下：dashDot','dashDotDot', 'dashed','dotted','double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot','mediumDashed', 'slantDashDot', 'thick', 'thin'
"""
cell = sheet.cell(9, 2)
cell.border = Border(
top=Side(style="thin", color="FFB6C1"),
bottom=Side(style="dashed", color="FFB6C1"),
left=Side(style="dashed", color="FFB6C1"),
right=Side(style="dashed", color="9932CC"),
diagonal=Side(style="thin", color="483D8B"), # 对角线
diagonalUp=True, # 左下 ~ 右上
diagonalDown=True # 左上 ~ 右下
)
wb.save("p2.xlsx")
"""
# 6.字体
"""
cell = sheet.cell(5, 1)
cell.font = Font(name="微软雅黑", size=45, color="ff0000", underline="single")
wb.save("p2.xlsx")
"""
# 7.背景色
"""
cell = sheet.cell(5, 3)
cell.fill = PatternFill("solid", fgColor="99ccff")
wb.save("p2.xlsx")
"""
# 8.渐变背景色
"""
cell = sheet.cell(5, 5)
cell.fill = GradientFill("linear", stop=("FFFFFF", "99ccff", "000000"))
wb.save("p2.xlsx")
"""
# 9.宽高（索引从1开始）
"""
sheet.row_dimensions[1].height = 50
sheet.column_dimensions["E"].width = 100
wb.save("p2.xlsx")
"""
# 10.合并单元格
"""
sheet.merge_cells("B2:D8")
sheet.merge_cells(start_row=15, start_column=3, end_row=18, end_column=8)
wb.save("p2.xlsx")
"""
"""
sheet.unmerge_cells("B2:D8")
wb.save("p2.xlsx")
"""
# 11.写入公式
"""
sheet = wb.worksheets[3]
sheet["D1"] = "合计"
sheet["D2"] = "=B2*C2"
wb.save("p2.xlsx")
"""
"""
sheet = wb.worksheets[3]
sheet["D3"] = "=SUM(B3,C3)"
wb.save("p2.xlsx")
"""
# 12.删除
"""
# idx，要删除的索引位置
# amount，从索引位置开始要删除的个数（默认为1）
sheet.delete_rows(idx=1, amount=20)
sheet.delete_cols(idx=1, amount=3)
wb.save("p2.xlsx")
"""
# 13.插入
"""
sheet.insert_rows(idx=5, amount=10)
sheet.insert_cols(idx=3, amount=2)
wb.save("p2.xlsx")
"""
# 14.循环写内容
"""
sheet = wb["Sheet"]
cell_range = sheet['A1:C2']
for row in cell_range:
for cell in row:
cell.value = "xx"
for row in sheet.iter_rows(min_row=5, min_col=1, max_col=7, max_row=10):
for cell in row:
cell.value = "oo"
wb.save("p2.xlsx")
"""
# 15.移动 #正整数表示向下或向右，负整数为向上或向左
"""
# 将H2:J10范围的数据，向右移动15个位置、向上移动1个位置
sheet.move_range("H2:J10",rows=-1, cols=15)
wb.save("p2.xlsx")
"""
"""
sheet = wb.worksheets[3]
sheet["D1"] = "合计"
sheet["D2"] = "=B2*C2"
sheet["D3"] = "=SUM(B3,C3)"
sheet.move_range("B1:D3",cols=10, translate=True) # 自动翻译公式
wb.save("p2.xlsx")
"""
# 16.打印区域
"""
sheet.print_area = "A1:D200"
wb.save("p2.xlsx")
"""
# 17.打印时，每个页面的固定表头
"""
sheet.print_title_cols = "A:D"
sheet.print_title_rows = "1:3"
wb.save("p2.xlsx")
"""
  压缩文件
    需要导入 shutil
    示例
      import shutil
# 1. 压缩文件
"""
# base_name，压缩后的压缩包文件
# format，压缩的格式，例如："zip", "tar", "gztar", "bztar", or "xztar".
# root_dir，要压缩的文件夹路径
"""
# shutil.make_archive(base_name=r'datafile',format='zip',root_dir=r'files')
# 2. 解压文件
"""
# filename，要解压的压缩包文件
# extract_dir，解压的路径
# format，压缩文件格式
"""
# shutil.unpack_archive(filename=r'datafile.zip', extract_dir=r'xxxxxx/xo', format='zip')
  路径相关
    转义
      在windows系统中如果有这样的一个路径 `D:\nxxx\txxx\x1`，程序会报错。因为在路径中存在特殊符 `\n`（换行符）和`\t`（制表符），Python解释器无法自动区分
      - 加转义符，例如：`"D:\\nxxx\\txxx\\x1"`
- 路径前加r，例如：`r"D:\\nxxx\\txxx\\x1"`
    程序当前路径
      相对路径
        在`/Users/wupeiqi/PycharmProjects/CodeRepository/`路径下编写 `demo.py`文件
        在终端运行时的操作
          操作一
            文件会创建在 `/Users/wupeiqi/PycharmProjects/CodeRepository/` 目录下
            cd /Users/wupeiqi/PycharmProjects/CodeRepository/
python demo.py
          操作二
            文件会创建在 `/Users/wupeiqi`目录下
            cd /Users/wupeiqi
python
/Users/wupeiqi/PycharmProjects/CodeRepository/demo.py
    文件和路径相关（os模块的使用）
      import shutil
import os
# 1. 获取当前脚本绝对路径
"""
abs_path = os.path.abspath(__file__)
print(abs_path)
"""
# 2. 获取当前文件的上级目录
"""
base_path = os.path.dirname( os.path.dirname(路径) ）
print(base_path)
"""
# 3. 路径拼接
"""
p1 = os.path.join(base_path, 'xx')
print(p1)
p2 = os.path.join(base_path, 'xx', 'oo', 'a1.png')
print(p2)
"""
# 4. 判断路径是否存在 #不能精确到某个应用，只能文件夹
"""
exists = os.path.exists(p1)
print(exists)
"""
# 5. 创建文件夹
"""
os.makedirs(路径)
"""
"""
path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
if not os.path.exists(path):
os.makedirs(path)
"""
# 6. 是否是文件夹
"""
file_path = os.path.join(base_path, 'xx', 'oo', 'uuuu.png')
is_dir = os.path.isdir(file_path)
print(is_dir) # False
folder_path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
is_dir = os.path.isdir(folder_path)
print(is_dir) # True
"""
# 7. 删除文件或文件夹
"""
os.remove("文件路径")
"""
"""
path = os.path.join(base_path, 'xx')
shutil.rmtree(path)
"""
# 8. 拷贝文件夹
"""
shutil.copytree("/Users/wupeiqi/Desktop/图/csdn/","/Users/wupeiqi/PycharmProjects/CodeRepository/files")
"""
# 9.拷贝文件
"""
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/")
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/x.png")
"""
# 10.文件或文件夹重命名
"""
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/xxxx.png")
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/files","/Users/wupeiqi/PycharmProjects/CodeRepository/images")
"""