目录

Day 4 本节内容:

一、生成器(只有被调用时才会生成对应数据;将函数创建为生成器可以随时中断函数,去同时做一些其他的功能,然后再进入函数继续执行。)

1.列表生成式

2.生成器定义

3.生成器创建方法1:

4.生成器创建方法2:

5.生成器用例:通过yield实现在单线程的情况下实现并发运算的效果,异步IO

6.额外知识点:异常处理中try,except用法。

二、迭代器(可以被next()函数调用并不断返回下一个值的对象称为迭代器,生成器也是一个迭代器)

1.定义

2.小结

三、装饰器(不修改被装饰函数的源代码和调用方式,实现附加功能的添加)

1.定义:

2.原则:函数感知不到装饰器的存在

3.实现装饰器知识点:1.函数即变量 2.高阶函数 3.嵌套函数

四、内置函数

五、json & pickle数据序列化

1.序列化与反序列化定义,及json和pickle的使用

2.json与pickle的区别(属性用法一致)

六、软件目录结构规范

七、作业:


Day 4 本节内容:

1.迭代器 & 生成器

2.装饰器

3.内置函数

4.json & pickle数据序列化

5.软件目录结构规范

6.作业:ATM项目开发

一、生成器(只有被调用时才会生成对应数据;将函数创建为生成器可以随时中断函数,去同时做一些其他的功能,然后再进入函数继续执行。

1.列表生成式

#使用列表生成式创建列表 [2,4,6,8,10]
# 方式1.
list1 = [a * 2 for a in range(1,6)]
print(list1)# 方式2.
def functest(num):b = 2return(num * b)
list2 = [functest(a) for a in range(1,6)]

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,直接创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

2.生成器定义

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

列表表达式:直接把列表所有元素全部生成;

生成器:1.只有在调用时才会生成对应数据

               2.只记录当前位置,不可取前面过去的数据

               3.只有一个 __next__() 方法,用来获取下一个数据;python2中为 next()

3.生成器创建方法1:

只要把一个列表生成式的[]改成(),就创建了一个generator:

# 创建生成器
generator = (a * 2 for a in range(1,6))
print(generator) # <generator object <genexpr> at 0x00000190A44D94A0># 调用生成器
generator.__next__() # 第一次调用,值为2,但是不会输出
print(generator.__next__()) # 第二次调用,输出4
print('hello') # hello
for i in generator:print(i) # 输出 6 8 10,因为生成器只记录当前位置,不会重新调用

4.生成器创建方法2:

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。将函数变为生成器的好处,可以随时中断函数(去同时做一些其他的功能)然后再进入函数继续执行。

def func():for n in range(1,4):print('hello')yieldprint(f'你好这是生成器{n}')gener = func()
gener.__next__()
gener.__next__()
gener.__next__()# 执行逻辑
1. def func() 定义函数
2. gener = func() 创建生成器gener
3. gener.__next__() # n=1;---输出“hello”;---遇到yield中断退出
4. gener.__next__() # 返回继续执行生成器;---输出“你好这是生成器1”;---n=2;---输出“hello”;---遇到yield中断退出
5. gener.__next__() # 返回继续执行生成器;---输出“你好这是生成器2”;---n=3;---输出“hello”;---遇到yield中断退出

yield:保存当前生成器函数状态并可以返回值(yield 10,类似return 10),中断函数退出。

__next__() :回来继续执行生成器函数

send():可以传递值给yield,然后回来继续执行生成器函数

5.生成器用例:通过yield实现在单线程的情况下实现并发运算的效果,异步IO

#通过生成器实现协程并行运算,吃包子和做包子两个配合同步进行
import time
def consumer(name):print("%s 准备吃包子啦!" %name)while True: # 确保生成器可以一直被调用执行下去baozi = yieldprint("包子[%s]来了,被[%s]吃了!" %(baozi,name))def make():c1 = consumer('A')c2 = consumer('B')c1.__next__()c2.__next__()print("老子开始准备做包子啦!")for i in range(1,4):time.sleep(1)print("做了2个包子!")c1.send(i)c2.send(i)make()

6.额外知识点:异常处理中try,except用法。

每当在运行时检测到程序错误时,python就会引发异常。对待异常有两种方法:一是可以在程序中捕捉和响应错误;或者忽略已发生的异常。

例如:生成器如果只有3个数据,通过next方法取多余的数据,例第4个数据,就会抛出一个异常 StopIteration。

 例:通过异常处理,使程序运行不报错。

def func():for n in range(1,4):print('hello')yield 10print(f'你好这是生成器{n}')return '这里遇到一个生成器调用错误信息!!!'
gener = func()
while True:try:gener.__next__()gener.__next__()gener.__next__()gener.__next__()except StopIteration as errorinfo:print('程序运行返回StopIteration错误:',errorinfo.value)break

二、迭代器(可以被next()函数调用并不断返回下一个值的对象称为迭代器,生成器也是一个迭代器

1.定义

我们已经知道,可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如listtupledictsetstr等;一类是generator,包括生成器和带yield的generator function。

1.1这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

#  可以使用isinstance()判断一个对象是否是Iterable可迭代对象:

from collections.abc import Iterable
isinstance([1,2,3],Iterable)
# True
isinstance(100,Iterable)
# False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

1.2可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

#  可以使用isinstance()判断一个对象是否是Iterator对象:

from collections.abc import Iterable,Iterator
isinstance([1,2,3],Iterator)
# False
isinstance((x for x in range(10)),Iterator)
# True

2.小结

凡是可作用于for循环的对象都是Iterable可迭代对象类型;

凡是可作用于next()函数的对象都是Iterator迭代器类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstrIterable可迭代对象不是Iterator迭代器,不过可以通过iter()函数获得一个Iterator对象。

from collections.abc import Iterable,Iterator
isinstance([1,2,3],Iterator)
#  False
num = iter([1,2,3]) # 通过iter()函数,将可迭代对象列表变为迭代器
isinstance(num,Iterator)
# True
print(num.__next__()) # 1
print(num.__next__()) # 2

三、装饰器(不修改被装饰函数的源代码和调用方式,实现附加功能的添加)

1.定义:

本质是函数(装饰其它函数),就是为其他函数添加附加功能。

高阶函数 + 嵌套函数 =》装饰器

2.原则:函数感知不到装饰器的存在

        *1.不能修改被装饰的函数的源代码

        *2.不能修改被装饰的函数的调用方式

3.实现装饰器知识点:1.函数即变量 2.高阶函数 3.嵌套函数

        *1.函数即变量:函数调用之前都有定义就可以执行成功(定义函数相当于定义一个变量,将函数体在内存中指向一个地址;)

定义、调用变量
x = 1
y = 2
print(y,x)定义、调用函数
def x():print(1)y()def y():print(2)
x()
# 1
# 2

        *2.高阶函数:a.把一个函数名当做实参传给另外一个函数(在不修改被装饰函数的源代码情况下为其添加功能);b.返回值中包含函数名(不修改被装饰函数的调用方式)

a.在不修改被装饰函数的源代码情况下为其添加功能
import time
def func1():time.sleep(3)print('in the func1')def func2(func_name):start_time = time.time()func_name()stop_time = time.time()print(f'the func1 run time is {stop_time-start_time} !')
func2(func1)# 输出
in the func1
the func1 run time is 3.0013177394866943 !
# 实现一个查看程序运行时间的附加功能############################################################b.不修改被装饰函数的调用方式
import time
def func1():print('in the func1')def func2(func_name):print('hello')return func_name1.
func2(func1) #相当于要实现的附加功能
#输出
hello2.
test1 = func2(func1)
print(test1) #实现附加功能,同时返回要被装饰函数func1的内存地址 用于调用函数
#输出
hello
<function func1 at 0x000002AD8F68E160>test1() #调用test1,同时实现附加功能和被修饰函数的功能
#输出
hello
in the func1***3.覆盖掉要被装饰的函数,实现不改变其调用方式
func1 = func2(func1)
func1()
#输出
hello
in the func1

        *3.嵌套函数:在一个函数体内创建另外一个函数(创建新函数,并非调用),这种函数就叫嵌套函数。

高阶函数 + 嵌套函数 =》装饰器

例:装饰器timer,为test1和test2函数增加显示运行时间的功能import time
# 嵌套函数 + 高阶函数 = 组成装饰器 timer
def timer1(func):def deco():start_time = time.time()func()stop_time = time.time()print('the function run time is %s'% (stop_time-start_time))return deco@timer1 # 调用timer装饰器装饰test1函数;相当于执行 test1=timer(test1)
def test1():time.sleep(3)print('It is test1 func')
test1()# 执行步骤:
# 1.定义函数timer --》 2.执行@timer,相当于执行test1 = timer(test1) --》
# 3.test1 = timer(test1) = deco;返回deco函数的内存地址 --》4.执行test1() = 执行deco()
_____________________timer1_____________________注意:
当test1函数无参数,test2函数需要参数时,用装饰器同时装饰这两个函数;给deco函数添加'*args,**kwargs'参数def timer2(func):def deco(*args,**kwargs):start_time = time.time()func(*args,**kwargs)stop_time = time.time()print('the function run time is %s'% (stop_time-start_time))return deco@timer2
def test2(name,age):time.sleep(3)print('It is test2 func')print(name,age)test2('XiaoZz',18)@timer2
def test3():time.sleep(3)print('It is test3 func')test3()
_____________________timer2_____________________

装饰器例子:

# 给三个页面添加登录验证功能,并且使用不同的登录方式
# 1.定义三种不同登录方式的账号密码
local_name,local_password = 'local','password'
ldap_name,ldap_password = 'ldap','password'
other_name,other_password = 'other','password'# 2.定义装饰器login函数,添加参数login_type判断不同的登录方式
def login(login_type):# 一级嵌套函数,func参数传递'被装饰的函数'def out_deco(func):# 二级嵌套函数,实现新增的功能与'被装饰的函数'功能def deco(*args,**kwargs):user_username = input('请输入你的用户名称:').strip()user_password = input('请输入你的用户密码:').strip()# 判断不同的登录方式,实现不同的效果if login_type == 'local':default_username = local_namedefault_password = local_passwordif user_username == default_username and user_password == default_password:print('欢迎你,登陆成功!')func(*args,**kwargs) # 执行被装饰参数else:print('账号或密码错误,退出!')return# 判断不同的登录方式,实现不同的效果if login_type == 'ldap':default_username = ldap_namedefault_password = ldap_passwordif user_username == default_username and user_password == default_password:print('欢迎你,登陆成功!')func(*args,**kwargs) # 执行被装饰参数else:print('账号或密码错误,退出!')return# 判断不同的登录方式,实现不同的效果if login_type == 'other':default_username = other_namedefault_password = other_passwordif user_username == default_username and user_password == default_password:print('欢迎你,登陆成功!')func(*args,**kwargs) # 执行被装饰参数else:print('账号或密码错误,退出!')returnreturn deco # 返回deco函数内存地址return out_deco # 返回out_deco函数内存地址@login(login_type='local') # 添加参数login_type选择不同的对应登录方式
def page1():print('hello,there is page 1 !')
print('---------- page1 function local ----------')
page1()@login(login_type='ldap') # page2 = login()
def page2(name):print('hello %s,there is page 2 !!' % name)
print('---------- page2 function ldap ----------')
page2('XiLong')@login(login_type='other')
def page3(name1,name2):print('hello %s and %s,there is page 3 !!!' %(name1,name2))
print('---------- page3 function other ----------')
page3('XiaoLong','XiaoJin')

四、内置函数

1.数学运算

abs:求数值的绝对值

>>> abs(-2)
2

max/min:返回可迭代对象中的元素中的最大值/ 最小值 或者所有参数的最大值/ 最小值

>>> max(1,2,3) # 传入3个参数 取3个中较大者
3
>>> max('1234') # 传入1个可迭代对象,取其最大元素值
'4'
>>> max(-1,0) # 数值默认去数值较大者
0
>>> max(-1,0,key = abs) # 传入了求绝对值函数,则参数都会进行求绝对值后再取较大者
-1

pow:返回两个数值的幂运算值或其与指定整数的模值

>>> pow(2,3)
>>> 2**3
8
>>> pow(2,3,5)
>>> pow(2,3)%5
>>> 2**3%5
3

round:对浮点数进行四舍五入求值

>>> round(1.1314926,1)
1.1
>>> round(1.1314926,5)
1.13149

sum:对元素类型是数值的可迭代对象中的每个元素求和

# 传入可迭代对象
>>> sum((1,2,3,4))
10
# 元素类型必须是数值型
>>> sum((1.5,2.5,3.5,4.5))
12.0
>>> sum((1,2,3,4),-9)
1

2.类型转换

bool:根据传入的参数的逻辑值创建一个新的布尔值

>>> bool() #未传入参数
False
>>> bool(0) #数值0、空序列等值为False
False
>>> bool(1)
True

int:根据传入的参数创建一个新的整数

>>> int() #不传入参数时,得到结果0。
0
>>> int(3)
3
>>> int(3.6)
3

float:根据传入的参数创建一个新的浮点数

>>> float() #不提供参数的时候,返回0.0
0.0
>>> float(3)
3.0
>>> float('3')
3.0

str:返回一个对象的字符串表现形式(给用户)

>>> str()
''
>>> str(None)
'None'
>>> str('abc')
'abc'
>>> str(123)
'123'

bytearray:根据传入的参数创建一个新的字节数组

>>> bytearray('中文','utf-8')
bytearray(b'\xe4\xb8\xad\xe6\x96\x87')

bytes:根据传入的参数创建一个新的不可变字节数组

>>> bytes('中文','utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'

ord:返回Unicode字符对应的整数

>>> ord('a')
97

chr:返回整数所对应的Unicode字符

>>> chr(97) #参数类型为整数
'a'

bin:将整数转换成2进制字符串

>>> bin(3)
'0b11'

oct:将整数转化成8进制数字符串

>>> oct(10)
'0o12'

hex:将整数转换成16进制字符串

>>> hex(15)
'0xf'

iter:根据传入的参数创建一个新的可迭代对象

tuple:根据传入的参数创建一个新的元组

list:根据传入的参数创建一个新的列表

set:根据传入的参数创建一个新的集合

frozenset:根据传入的参数创建一个新的不可变集合

enumerate:根据可迭代对象创建枚举对象

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1)) #指定起始值
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

3.序列操作

all:判断可迭代对象的每个元素是否都为True值

any:判断可迭代对象的元素是否有为True值的元素

sorted:对可迭代对象进行排序,返回一个新的列表

>>> a = ['a','b','d','c','B','A']
>>> a
['a', 'b', 'd', 'c', 'B', 'A']>>> sorted(a) # 默认按字符ascii码排序
['A', 'B', 'a', 'b', 'c', 'd']>>> sorted(a,key = str.lower) # 转换成小写后再排序,'a'和'A'值一样,'b'和'B'值一样
['a', 'A', 'b', 'B', 'c', 'd']

4.对象操作

help:返回对象的帮助信息

dir:返回对象或者当前作用域内的属性列表

id:返回对象的唯一标识符

hash:获取对象的哈希值

type:返回对象的类型,或者根据传入的参数创建一个新的类型

len:返回对象的长度

ascii:返回对象的可打印表字符串表现方式

vars:返回当前作用域内的局部变量和其值组成的字典,或者返回对象的属性列表

内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii

五、json & pickle数据序列化

1.序列化与反序列化定义,及json和pickle的使用

序列化:将对象转换成易传输或易保存的数据的过程称为序列化;即把Python对象转换成JSON字符串。例如将内存上的对象变成字符串可以存到硬盘上。

json.dumps()

json.dump() 将Python对象转换成json字符串并存储到文件中

反序列化:将一定的数据格式转成可用的对象称为反序列化;即把JSON字符串转换成python对象。例如将硬盘上的数据对象变成原来格式,可以再恢复到内存中。

json.loads()

json.load() 读取指定文件中的json字符串并转换成Python对象

# 如果需要序列化的数据中存在中文,就需要将dumps方法的 ensure_ascii 参数设置为False,否则显示的中文会乱码。
import json
# 序列化:
name = ['校长','老师']
test1 = json.dumps(name,ensure_ascii=False)
print(test1) #["校长", "老师"]
print(type(test1)) # <type 'str'>file = open('abc.txt','w')
json.dump(name,file)# 反序列化
test2 = json.loads(test1)
print(test2[0]) # 校长
print(type(test2)) #<type 'list'>json.load(file)

2.json与pickle的区别(属性用法一致)

json序列化之后得到的是字符串,仅支持字典和列表对象,各种编程语言几乎都能支持 json。
pickle序列化之后得到的是字节,支持Python中大部分对象,仅被Python支持
pickle序列化不会改变字典键的数据类型;json序列化,如果键是数字会转为字符串

六、软件目录结构规范

1.例如开发一个 App 项目,目录结构应该如下:

App/
|-- bin/ # 存放项目的一些可执行文件
|   |-- app.py # 项目启动脚本,调用程序入口 main.py
|
|-- app/ # 存放项目的所有源代码
|   |-- tests/ # 存放单元测试代码
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py # 程序的入口
|
|-- conf/ # 存放一些配置文档
|   |-- app_conf.py # 配置文件
|   |-- abc.rst
|
|-- log/ # 存放日志文件
|   |-- app.log # 运行日志
|   |-- error.log # 错误日志
|
|-- setup.py # 安装、部署、打包的脚本
|-- requirements.txt # 存放软件依赖的外部Python包列表
|-- README # 项目说明文件

2.如何通过 bin/app.py 启动脚本去直接调用 main.py 呢?

# 在 bin/app.py 中
import os
import sys
print(__file__) # 打印相对路径
print(os.path.abspath(__file__)) # 打印绝对路径
print(os.path.dirname(os.path.abspath(__file__))) # 获取路径名,去掉文件名
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 再使用一次path.dirname方法获取上一级路径sys.path.append(BASE_DIR) # 添加环境变量,这时就可以直接调用 main.py 和 app_conf.py 中的函数了
from conf import app_conf
from app import main

3.注意:不应当在代码中直接 import 来使用"app_conf.py"或"main.py"。应该可以通过给 main.py启动参数 -c 指定配置路径的方式来让程序读取配置内容。

七、作业:

作业需求:模拟实现一个ATM + 购物商城程序

  1. 额度 15000或自定义
  2. 实现购物商城,买东西加入 购物车,调用信用卡接口结账
  3. 可以提现,手续费5%
  4. 每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
  5. 支持多账户登录
  6. 支持账户间转账
  7. 记录每月日常消费流水
  8. 提供还款接口
  9. ATM记录操作日志
  10. 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
  11. 用户认证用装饰器

示例代码 https://github.com/triaquae/py3_training/tree/master/atm 

简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329

Python运维开发从入门到精通学习 Day4相关推荐

  1. python web开发入门_python大佬整理的python web开发从入门到精通学习笔记

    原标题:python大佬整理的python web开发从入门到精通学习笔记 Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通 ...

  2. 阅后即焚,Python 运维开发99速成

    2019独角兽企业重金招聘Python工程师标准>>> -欢迎大家订阅微信公众号:Python从程序猿到程序员 导读 本文篇幅较长,请收藏并耐心阅读 首先请读者原谅这个文章标题有些唬 ...

  3. Python运维开发基础01-语法基础【转】

    开篇导语 整个Python运维开发教学采用的是最新的3.5.2版,当遇到2.x和3.x版本的不同点时,会采取演示的方式,让同学们了解. 教学预计分为四大部分,Python开发基础,Python开发进阶 ...

  4. day01.介绍python运维开发

    第1节:介绍python运维开发 课程的开场白: 学完次课程可以开发出高效的自动化软件.运维监控.聊天软件.网站等内容. 这个运维开发跟实际上的开发是有区别的,区别在我们是实现功能,但是不能向开发那样 ...

  5. Python运维开发工程师养成记(循环语句)

    图示 循环语句类型 while循环 for循环 嵌套循环 循环控制语句 break语句:在语句块执行过程中终止循环,并且跳出整个循环 continue语句:在语句块执行过程中终止当前循环,跳出该次循环 ...

  6. Python运维开发基础09-函数基础【转】

    上节作业回顾 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 实现简单的shell命令sed的替换功能import s ...

  7. Python运维开发基础10-函数基础【转】

    一,函数的非固定参数 1.1 默认参数 在定义形参的时候,提前给形参赋一个固定的值. #代码演示: def test(x,y=2): #形参里有一个默认参数 print (x) print (y) t ...

  8. Revit二次开发从入门到精通学习之路, (含Revit二次开发教程下载)

    Revit二次开发从入门到精通学习之路 Autodesk Joe Ye叶雄进 2. 18 2014    yexiongjin@hotmail.com Revit在国内的应用越来越广泛, Revit ...

  9. 第1课 EOS开发从入门到精通学习导航

    第1课 EOS开发从入门到精通学习导航 柚子(EOS)可以理解为Enterprise Operation System,即为商用分布式应用设计的一款区块链操作系统.EOS是EOS软件引入的一种新的区块 ...

最新文章

  1. 大数据时代 | 数据分析方法及理论详解
  2. leetcode 504. 七进制数(Java版)
  3. 怎样在sqlite3上执行SQL语句
  4. [MySQL]关于amd.dll后门病毒入侵3306端口的临时解决方案
  5. uva 10559——Blocks
  6. thinkcmf ajax,thinkcmfx 中如何用jquery ajax提交数据,自己尝试去做之后,还是没法提交,求助!...
  7. (王道408考研操作系统)第二章进程管理-第三节2:实现进程互斥的软件方法
  8. outlook客户端接收邮件报错0x80040600
  9. 编译OpenJDK8:Your cygwin is too old. You are running but at least cygwin 1.7 is required
  10. 《Shell脚本学习指南》笔记--2011-12-17
  11. mysql是用啥语言写的_mysql源码是什么语言
  12. 电影海报页面设计Html5,如何设计电影海报
  13. 史上最全后端技术介绍
  14. 新版白话空间统计(24):中位数中心
  15. 意在寥廓观鸿蒙 什么意思,“滴滴寒露凋芙蓉”的意思及全诗出处和翻译赏析...
  16. java存根_Java方法存根
  17. 机器学习环境配置(Tesla K80安装PyTorch的全过程)
  18. “为了买台手机,研究大半个月后仍然无从选择”
  19. 文章开始同步到我的微信公众号
  20. PHP除数取余数,php相除取余数的实现方法

热门文章

  1. css:hover改变另一个元素的样式
  2. 直方图均衡化(Histogram equalization)与直方图规定化
  3. Coursera无法播放视频问题的解决
  4. CS党必须了解的P/NP常识
  5. struts的框架介绍
  6. 【python】matplotlib.pyplot介绍
  7. 魔兽争霸Trigger学习教程(0)
  8. 矩阵理论复习(十二)
  9. WAS上配置数据源连接失败
  10. ! LaTeX Error: File xxx.sty not found-统一解决办法