01.Python基础

Python爬虫快速实战

目标: 通过四天的学习,具备爬虫(Python)初级工程师的能力,胜任接口(API)自动化测试、数据采集(爬虫)的岗位要求。

第一天:Python基础

Python的编程环境: 3.6/3.7

交互式环境:命令行(cmd)中直接输入python 【回车】后进入交互式环境,此环境中直接执行python指令。

脚本: 通过记事本(文本编缉器)编写python脚本文件,通过python解释器环境(Pycharm工具)来执行脚本,并显示执行的结果。

1.1 数据类型

1.1.1 基本数据类型

int 整型, 如1, 2, 3等
float 浮点(小数)类型, 如1.15, 3.14等
str 字符类型, 如'祁老师真帅', "disen在西安校区等你"。单引号或双引号必段是英文字符。
bytes 字节类型, 如 b'123', b'abc'。字节类型用于文件流、网络流的传输。通过str.encode()函数,将字符串转成字节数据,一般指定encode()函数中的encoding参数,表示字符的编码类型,默认UTF-8(Python3.7)。可以通过bytes.decode(encoding=None)函数,将字节数据转成字符串。
bool 布尔类型, True 真, False假, 可以通过int(bool值)转成整型,False ->0 ,  True -> 1, 通过bool(obj)将obj对象转换成布尔值。

【注意】None 类似于C的空指针,表示未知。SyntaxError语法错误(异常)。

type(obj)函数主要获取obj对象的类型, 属于Python自省函数的成员,也是创建类对象的类(元类)。

help(obj.func_name) 查看对象的某一函数的帮助文档, 如 help(bytes.decode), help(str.encode), 按Q键退出。

练习:

bool(0)   -> False
bool('')  -> False
bool('a') -> True
bool(None) -> False

1.1.2 集合类型

由多个基本类型的数据组成的对象的结构,称之为集合类型。

list 列表类型, 如 [1,2, 3], 和c或java语言中列表或数组有区别,列表中的元素可以是任意类型,而数组的类型是固定的。
tuple 元组, 如(1, 2, 3)
dict 字典, 如{‘id’: '1001', 'name': 'disen'}
# list元素类型的示例
person = ['1001', 'disen', 20, 190, 99.5, ['编程', '看电影', '约会']]

# 索引操作, obj[索引下标], Python的索引下标从0开始(从左开始),负号表示从右开始(-1 开始)。 
person[0]
person[2]
person[-1] 最后一个元素
person[-1][-1]  获取最后一个列表元素中的最后一个元素
person[-2] 倒数第二个元素

# 索引切片操作  obj[起始位置:结束位置:步长],  默认: 起始为0, 结束为最后一个元素, 步长为1, 如果是-1表示,从右到左切片操作。
person[::-1]  
person[2:] 结果是 [20, 190, 99.5, ['编程', '看电影', '约会']]
person[2:4] 结果是 [20, 190]  不包含4索引下标的内容,即[2, 4)区间。
person[2:-1] 结果是 [20, 190, 99.5], 即不包含最后一个元素。

# 元素操作, append(元素)追加元素, remove(元素)删除元素, insert(index, 元素), pop(index)
person.insert(0, 1) 向索引下标0位置插入一个 1元素,原来的0下标元素整体向右移动。
person.pop(0)  弹出索引下标0位置的元素
person.remove('disen') 将disen元素从列表中删除
person.append('disen') 将disen的元素内容追加到列表的末尾。

# 修改元素
person[2] += 2  # 支持修改元素
# tuple 在Python中表示一个集合常量,只有声明时才能添加元组中的元素。定义之后的元组的元素不能修改。
person2 = (1, '1001', 'disen', 190, 99.5)

# 索引操作同列表的索引操作
person2[0]
person2[2:3]  结果 ('disen',) 元组,  只有一个元素时不能写成('disen'),因为它是优先表达式

person2[0] = 2  # 元组中的元素不能被修改, 解释器会抛出 TypeError异常错误。
# dict 字典 类似于json格式(JavaScript对象)【重点】
# 键 key,  值 value,  dict的结构称之为键值对
person = {'name': '永琪', 'sex': '男', 'height': 190, 'salary': 100.0} 

# 修改字典中的key的值  obj[key] = value
person['name'] = '凌永琪'
person["height"] = 200 # 单位: cm

# 获取key的值,  obj[key],  obj.get(key, default_value)
person["salary"]  # person['salary']
person.get('salary')  返回value

# .get()获取key,如果不存在key,则返回None,当然指定返回的default_value默认值
# [key] 获取key时,如果不存在key, 则解释器抛出异常(KeyError),程序中断执行(发生错误之后)
person['salaries']
person.get('salaries')
person.get('salaries', 90)

# dict对象常用的方法
.values()  返回所有value的列表
.keys()  返回所有key的列表
.items() 返回(key, value)组成的元组结构的列表
.setdefault(key, default_value) 设置key的默认值,如果key存在,则无效, 反之key不存在时,有效。
.pop(key)  弹出key, 实际上从dict中删除key
.update(dict) 通过给定的dict参数更新原有的字典。可以达到两个字典合并的效果。
.clear() 清空字典中所有item项目(key, value)
.copy() 复制字典,备份字典
【扩展】将dict字典对象转成json格式的字符串(json object {},   json array [])
json模块, 导入模块 `import 模块名`
json.dumps(dict) 转化为json格式的字符串
json.loads(json_str) 将json格式的字符串转成dict或list对象

【扩展】dir(obj) 查看obj对象中所有属性和方法, 结合help(obj.属性或方法)查看用法说明。

练习:

1. 创建水果的字典, 水果信息包含水果的编号 、类型或品种、价格、产地、规格(单件或整箱)
>>> fruits={'编号':1,'类型':'苹果','价格':10,'产地':'西安','规格':'单件'}
2. 查看水果的产地, 并修改为'西安'或`延安`
>>> fruits['产地']
'西安'
>>> fruits['产地']='延安'
>>> fruits['产地']
'延安'
3. 备份水果的字典, 修改原水果的价格和规格
>>> new_fruits = fruits.copy()
>>> fruits['价格']=20
>>> fruits['规格']='整箱'
>>> fruits
{'编号': 1, '类型': '苹果', '价格': 20, '产地': '延安', '规格': '整箱'}

1.1.3 类型转换

字符类型 转换 其它类型

'123'  -> 123    int('123', base=10)  base表示进制(2进制, 8进制、10进制、16进制)
'1000' -> 8      int('1000', base=2)  
'0b1210' ->725520   int('0b1210', base=16)  

'1.75' -> 1.75      float('1.75')
'True' -> True      bool('True' )
'您好' -> bytes       '您好'.encode('utf-8')

其它类型转成字符串类型

str(obj)  将obj对象数据转成str类型, 如果obj是dict类型,转成str之后,可以通过eval()将字符串的字典对象提取出来,执行的结果赋值给指定标识符。

【扩展】eval()函数,把给定的字符串的内容作为Python有效的表达式执行。

eval("a=10+100")        # 报错,表达式中不能出现赋值语句。
a = eval("10+100")      # 等同于 a = 10+100

dict()函数和list()函数

list()将可迭代(Iteration)对象转成列表, 集合类型和str都是可迭代的。
dict(key=value, key2=value2) 将指定的参数和参数值转成字典

【注】dict也是可迭代的,默认迭代是keys。

>>> person2
{'name': '凌永琪', 'height': 160, 'salary': 100.0, 'weight': 50, 'sex': '女', 'lovies': ['看书', '跑步']}
>>> list(person2)
['name', 'height', 'salary', 'weight', 'sex', 'lovies']
>>> list(person2.keys())
['name', 'height', 'salary', 'weight', 'sex', 'lovies']
>>> list(person.values())
['凌永琪', 160, 100.0, 50, '女', ['看书', '跑步']]
>>> apple = dict(name='青苹果', price=9.5)
>>> apple
{'name': '青苹果', 'price': 9.5}

1.2 标识符与运算符

标识符(identifier)是指用来标识某个实体的一个符号,在不同的应用环境下有不同的含义。在计算机编程语言中,标识符是用户编程时使用的名字,用于给变量、常量、函数、类等命名,以建立起名称与使用之间的关系。

1.2.1 标识符的命名规则

标识符通常由字母和数字以及其它字符构成,在Python中变量名、脚本名、包名、函数名建议使用小写字符,遇到词组,则以下划线_相连,类名使用驼峰命名(首字母大写),如 SystemManager, ClientThread, ServerThread。

标识符的命名不能使用Python中的关键字,通过help('keywords')查看python中的关键字。

1.2.2 Python支持的运算符

算术运算符

+ 加法、
- 减法、
* 乘法、
** 次幂、
/ 除法取整和小数、
// 除法取整、 
% 取余数
+= ,  -= ,  *=,  **=,  /=,  //=,  %= 

a = (23+102-5*20)/15 LaTex数学语法

a=round( (2**3+10**2-5*20)/15, 2)  # round()四舍五入的函数

>>> a = (2**3 + 10**2 -5*20)/15
>>> a
0.5333333333333333
>>> a = round((2**3 + 10**2 -5*20)/15,2)
>>> a
0.53

关系运算符**

> 大于
>= 大于等于
< 小于
<= 小于等于
== 内容相等
!= 内容不相等
is 内容和内存地址都相等【了解】
is not 内容和内存地址都不相等

逻辑运算符

and 且的关系,and 两边的bool值都为True是,则结果True
or 或的关系,两边有一个存在True,则结果为True
not 取反, 对False取反则为True, 对True取反则为False

位运算符

将数值转成二进制数据,从低位向高位进行计算。

bin(数值) 将10进制的数值转成二制的字符串,如下所示:

b1, b2 = bin(9), bin(10)
 1001   9 
 1010   10
^
= 0011  3 
----------------------- 
 1001   9 
 1010   10
| 
=1011  11
----------------------- 
 1001   9 
 1010   10
&
=1000   8
| 位或, 同一位的两个数中任一个是1,则为1,反之为0
& 位与, 同一位的两个数都是1,则为1,反之为0
^ 位异或, 同一位的两个数不相同时为1,反之为0

|=
&=
^=

优先级

算术运算 优先于 位运算和关系运算
位运算   优先于 关系运算
关系运算 优先于 逻辑运算
小括号 ( ) 最优先
4+5 | 10 > 5 or 10//2 > 3

【注意】Python不支持自增++或自减--运算符。

1.3 分支与循环

1.3.1 if分支语句

if 逻辑表达式:
   表达式结果为True的语句块
else:
   表达式结果为False的语句块
n1 = int(input('输入一个数字:'))
if n1 > 10:
  print('输入的数字大于10')  # 行前向右缩进了2个空格
else:
  print('输入的数字小于10')  # 同一层的缩进空格的数量保持一致 
if 逻辑表达式:
   表达式结果为True的语句块
elif 逻辑表达式2:
   表达式2结果为True的语句块
elif 逻辑表达式3:
   表达式3结果为True的语句块
else:
   最近表达式结果为False的语句块
import random # 随机模块
# n1 = float(input('输入本次考试的成绩:'))
n1 = random.randint(0, 100)
if n1 > 90:
  print('A')
elif n1 > 80:
  print('B')
elif n1 > 70:
  print('C')
elif n1 > 60:
  print('D')
else:
  print('你的成绩太差了, 重学一年')

1.3.2 if分支表达式

在Python中的三元表达式使用 if 和else分支表达式,如下所示:

select_opt = int(input('输入你的选择: 0 退出, 1 重新开始'))
cmd = 'exit' if select_opt == 0 else 'restart'

类似于java中的三目运算符条件?为true值:为假的值

1.3.3 while循环

while 逻辑表达式:
  循环执行语句
  [改变条件表达式中变量值]
  [如果逻辑表达式直接为True,则存在退出循环的条件判断]
  [continue | break]
s,i = 0, 1
while i <= 10:
  s += i  # 计算10以内数值和
  i += 1
print(s)  # 55
# 练习: 计算100以内除了3的陪数以外的所有数的和
s, i = 0, 1
for i in range(100):
  if i % 3 == 0:
     continue
  else:
     s += i
      
print(s)
i,sum=1,0
while i<=100:
  if(i%3!=0):
    sum+=i
  i+=1
print(sum)

1.3.4 for循环

for 变量名 in 可迭代对象:
   循环执行的语句

for 循环一般用于迭代list、tuple这种可迭代对象的。也可以通过range()函数生成迭代器。

person = {'name': '凌永琪', 'height': 160, 'salary': 100.0, 'weight': 50, 'sex': '女', 'lovies': ['看书', '跑步']}

# 从person中每一次迭代获取的元素或内容赋值给 k 变量。k变量名可任意的有效字符。
for k in person:  # 字典对象迭代是它的所有的key
    value = person.get(k)
    # 判断value是否为int类型的数值
    # isinstance 和 type、 dir 三个函数都属于自省函数
    if isinstance(value, int):
       print('找到了value为int类型的内容', k, value)

【推导式】

列表的推导式:  [ 变量名 for 变量名 in range(0, 10) 或 可迭代的对象 [条件表达式] ]
生成器的推导式:( 变量名 for 变量名 in range(0, 10) 或 可迭代的对象 [条件表达式] ) ,生成器是可迭代的对象。generator类型名, 它的结构类似于栈, 只能被迭代一次。
# 练习: 从ns列中提取出5的倍数的数值
ns = [9, 10, 20, 45]
[ n for n in ns if n % 5 == 0]
# 练习: 随机产生100个学生的Python课程的成绩, 成绩的范围是[0, 100]
import random
python_scores = [ random.randint(0, 100) for i in range(100) ]  
print(python_scores)

# len(list|dict|tuple|str)  获取指定类型对象的长度, 用于list或str或tuple
print(len(python_scores))

【练习】元素位置互换问题: 将”08,45,68,56,44”想变成”80,54,86,65,44”

>>> [item.strip()[::-1] for item in a.split(',')]
['80', '54', '86', '65', '44']
>>> ','.join([item.strip()[::-1] for item in a.split(',')])
'80,54,86,65,44'

【提示】选择str.split(',') 以逗号分隔元素生成一个list列表,再通过推导式和str的索引切片操作,将内容互换,再通过','.join(可迭代对象)将列表中的互换位置之后的元素以逗号的方式拼接成一个字符串。

参考以下的代码

>>> s = '2, 22, 78, 61, 21'
>>> s.split(',')
['2', ' 22', ' 78', ' 61', ' 21']
>>> [ item.strip()[::-1] for item in s.split(',') ]
['2', '22', '87', '16', '12']
>>> ','.join([ item.strip()[::-1] for item in s.split(',') ])
'2,22,87,16,12'
>>> 

str.strip()函数是删除字符串的两端的空白,类似于java中String对象的trim()函数。

>>> g1 = (i for i in range(0, 10))
>>> g1
 at 0x7fd3034b2a98>
>>> list(g1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(g1)
[]

【作业】获取str对象的方法,自我尝试使用str对象的相关方法(5到10个方法)。

# dir('abc')
# help(str.strip)
>>> dir(str)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> 'hellodisen'.capitalize()
'Hellodisen'
>>> 'hellodisen'.title()
'Hellodisen'
>>> 'redapple'.title()
'Redapple'
>>> 'red,apple'.title()
'Red,Apple'
>>> 'red,apple'.capitalize()
'Red,apple'
>>> 'abc.txt'.endswith('.txt')
True
>>> files = ['a.txt', 'b.png', 'c.txt']
>>> [filename for filename in files if filename.endswith('.txt')]
['a.txt', 'c.txt']
>>> 'a.txt'.replace('a', 'day02')
'day02.txt'
>>> 'hi,disen'.find('i')
1
>>> 'hi,disen'.rfind('i')
4
>>> 'hi,disen'.find('c')
-1
>>> 'hi,disen'.index('i')
1
>>> 'hi,disen'.rindex('i')
4
>>> 'hi,disen'.index('c')
Traceback (most recent call last):
  File "", line 1, in 
ValueError: substring not found
>>> 

1.4 函数定义与装饰器

函数作用: 封装复杂的功能代码,提高代码的重复使用率。

1.4.1 函数的定义

def 函数名([参数列表]):
  函数功能
  [return 函数结果]
# 定义计算某个数值内的和
def sum_n(n):
  s, i = 0, 1
  while i <= n:
    s += i
    i += 1
  return s
# 列出指定目录下的文件, 需要使用os模块
import os

def ls_files(dir_path):
  files = os.listdir(dir_path)  # 返回一个文件列表
  for filename in files:
    # os.path.join(dir, filename) 将文件名和目录拼接成文件的完整路径
    print( os.path.join(dir_path, filename) )
    # 没有return 语句,则默认返回None
>>> os.listdir('./sql')
['w18.sql', '4.sql', '5.sql', '2.sql', '3.sql', '1.sql', 'w8.sql']
>>> os.getcwd()
'/Users/apple'
>>> ls_files('./sql')
./sql/w18.sql
./sql/4.sql
./sql/5.sql
./sql/2.sql
./sql/3.sql
./sql/1.sql
./sql/w8.sql

./sql 指当前目录下的sql子目录(相对路径)。如果Window指定绝对路径r'd:/sql'

1.4.2 函数的参数

形参: 定义函数时声明的参数

实参: 调用函数时,传递的参数(值, 引用)

  • 值传参:标识符(变量)指向的内容在常量池(内存区), 如 a=100, sum_n(a)

  • 引用传参:标识符指向的内容在堆内存中, 如 l=[1, 2, 3], sum_list(l)

在调用函数时,可以按参数的位置从左到右依次传参(位置传值), 还可以指定参数名传参(关键参数传值)。

def sum_ns(n1, n2, n3, n4):
  print(n1, n2, n3, n4)
  return n1+n2+n3+n4
sum_ns(1, 2, 3, 4)  # 位置传值
sum_ns(4, n4=1, n2=10, n3=90)  # 关键参数传值,从使用关键参数方式传值的位置开始,后面所有的参数必须是关键参数的方式。

在声明或定义函数时,可以指定形参的默认值

def sum_ns(n1, n2, n3=10, n4=100):
  print(n1, n2, n3, n4)
  return n1+n2+n3+n4
sum_ns(1, 2)  # 传参时,带有默认值的参数,可以不用传值。
sum_ns(1,2, 1000, 10000)  # 带有默认值的参数,也可以传值

声明形参时,可以使用不定长参数

  • 位置参数 *args, args是元组类型
  • 关键参数 **kwargs , kwargs是字典类型
def sum_ns(*args):
  print(args)
  return sum(args)  # python内置求和函数 sum
sum_ns(1,2)  # 可以只传2个
sum_ns(1,2,3) # 可以只传3个, 也可以传更多的参数
# 通过字典对象的数据生成insert 插入的SQL语句
def save(table_name, **data):
   # insert into 表名(列名1, 列名2,...)  values(列值1, 列值2, ...)
   # str字符串的格式化(%、format) 
   sql = 'insert into %s(%s)  values(%s)'
   fields = ','.join(data.keys())
   values = []
   for k in data:
      v = data[k]
      # 判断数据类型, 如果字符类型(varchar),在mysql数据库中,加''单引号
      if isinstance(v, int) or isinstance(v, float):
        values.append("%s" % v)  # "1"
      else:
        values.append("'%s'" % v) # "'disen'"
   
   values = ','.join(values)
   full_sql = sql % (table_name, fields, values)
   print(full_sql)
   # pass # pass 表示省略代码(保留),保持函数结构的完整性
>>> '%s' % 'disen'
'disen'
>>> 'hi, %s' % 'jack'
'hi, jack'
>>> 'hi, %s, city at %s' % ('disen', '西安')
'hi, disen, city at 西安'
>>> 'hi, %(name)s,city at %(city)s' % {'name': 'jack', 'city': '西安'}
'hi, jack,city at 西安'
>>> 
save('tb_user', id=1, name='disen', age=20, salary=1000, years=10, height=180, city='西安', address="立人科技")

# insert into tb_user(id,name,age,salary,years,height,city,address)
# values (1, 'disen',20, 1000,10,180,'西安', '立人科技')
save('tb_store', id=1, name='disen爱心水果店', day_open='08:00', day_close='18:00')

# insert into tb_store(id,name,day_open, day_close)
# values (1, 'disen爱心水果店', '08:00', '18:00')

1.4.3 函数的生成器

# next在Python中存在一个内置的函数
def _next_(arr):
  for n in arr:
    yield n  # 将当前的函数转成生成器,产生一个可迭代的元素,好处:节省内存

生成器对象可以被for循环迭代, 也可以通过next(generator)获取生成器产生的元素,如果生成器中的元素不产出了,但强行执行next()函数时,则抛出StopIteration异常错误。

>>> ne = _next_([1,2,3,4])
>>> for n in ne:
...   print(n)
... 
1
2
3
4
>>> for n in ne:
...   print(n)
... 
>>> ne = _next_([1,2,3,4])
>>> next(ne)
1
>>> next(ne)
2
>>> next(ne)
3
>>> next(ne)
4
>>> next(ne)
Traceback (most recent call last):
  File "", line 1, in 
StopIteration
>>> 

1.4.4 函数装饰器

装饰器是一种设计模式(23种)思想, 主要解决扩展现有代码(程序)的功能,不会影响现有的功能。

import time
# 声明装饰器函数,如实现被装饰函数的运行时间
def runtime(func):
  def wrapper(*args, **kwargs):
    # print('--扩展功能--')
    start_time = time.time() # 获取当前的时间戳
    ret = func(*args, **kwargs)
    end_time = time.time() 
    print('耗时(秒)', round(end_time - start_time, 4))
    return ret
  return wrapper
# 使用装饰器函数

# 实现n的数值以内的所有数的和,e表示某个数陪数不参与计算
@runtime
def sum_n(n=100, e=None):
  s, i = 0, 1
  while i<=n:
    if e is not None:
      if i % e != 0:
         s += i
    else:
      s += i
    i += 1
    # 每一次循环,增加一点休眼时间
    time.sleep(0.01)
  return s

在Python中,某一个函数上方使用装饰器函数,则会按照闭包流程产生一个闭包函数,当调用被装饰的函数时,实际上调用是这个闭包函数(产生条件:内部函数持有外部函数的引用)。

1.5 类与对象

类: 由多个具有相同的属性(特征)和方法(行为)的对象抽象出来的类型

对象: 由类实例化出来的具体的事物。

1.5.1 定义类

# 定义学生类
# Python中所有类的父类(基类)都是object类
class Student(object):
  # 定义类的初始化实例属性的方法
  def __init__(self, sn, name, age, sex):
    # self 当前类的实例对象引用, 类似于java中类方法中this
    self.sn = sn  # 将方法的参数赋值给实例的属性(动态)
    self.name = name
    self.age = age
    self.sex = sex
    self.cources = []
    
  # 所有类的实例方法,第一个参数self表示当前的实例对象
  def select_cource(self, cource_name):
    self.cources.append(cource_name)
    
  # 取消选择课程
  def unselect_cource(self, cname):
    # 判断 cname课程名是否存在于当前已选择的课程中
    if cname in self.cources:
      self.cources.remove(cname)
    else:
      print('你取消的课程 %s 不存在于已选择的课程列表中' % cname)
    
  def show_info(self):
    print(self.sn, self.name, self.age, self.sex, self.cources)

1.5.2 类的实例化

s1 = Student('01', '婷婷', 21, '女')  # 先创建类的实例,再调用__init__()方法
s1.select_cource('Python')
s1.select_cource('Java')
s1.select_cource('H5')
s1.show_info()  # 调用对象方法时,无须考虑形参中的self参数,因为方法由对象调用的,当前对象自动传入到self

1.6 包与模块

包(package 文件目录)和模块(python脚本)属于项目工程的概念,如果一项工程非常庞大,可以借助包或模块拆分或细化软件工程,便于多人协作开发和代码维护。

包: 文件目录+__init__.py , __init__.py文件初始化当前的package包, 在此脚本声明的变量或编写的函数都属于单例模式,即无论包使用(导入)多少次,初始化脚本(__init__.py)只会执行一次。

导包: 导入 import 包名(文件目录名)

模块: 某一个包(package)下的一个python脚本。

zzserver(project)
   |--db (package)
        |-- __init__.py  
        |-- user_db.py
        |-- order_db.py
        |-- goods_db.py
   |--service
        |-- __init__.py
        |-- user_service.py
   |--utils
        |-- __init__.py
   |--view
        |-- __init__.py
   |--pay
        |-- __init__.py
   |--order
        |-- __init__.py
# user_db.py内容
class UserDB:
  pass

class User:
  pass
# order_db.py内容
class OrderDB:
  pass
# 在 user_service.py脚本中导入db包下的user_db.py中 UserDB类
from db.user_db import UserDB  # 同一个模块的导入场景
from db.user_db import User
from db.order_db import OrderDB  

1.7 with上下文与异常

在Python中,所有的对象都可以托管给上下文管理器,使用 with关键字,语法如下:

with obj [as 标识符]:
    # 进入上下文
    # 功能
    pass
# 退出上下文

【注意】obj托管上下文之前,obj所属的类必须 实现__enter____exit__两个方法,如果没有实现,则抛出异常。

上下文管理器作用: 确定程序正常执行,在代码执行之前,分配资源;在执行完成后,可以合理地回收资源。

当对象obj进入上下文时,调用obj.__enter__函数, 当退出上下文时,调用obj.__exit__函数。

class DB:
   def __enter__(self):
      print('--进入上下文--')
      self.conn = '已连接'
      return self.conn  # 可以没有返回
    
   def __exit__(self, except_type, msg, tb):
      print('--退出上下文--')
      self.conn = None
      if except_type:  # 存在异常错误
        print('存在异常', msg)
      return True # True表示如果出现的异常,则内部消化; False 表示如果出现异常,则由外部程序处理。
db = DB()
with db as c:  # as 指定标识符来接收enter函数返回结果或数据
  print(c)
  print(10/0)
  print('--gogogo---')
  
print(db.conn) # 退出上下文时,db.conn为None

1.8 文件操作

Python中文件读写操作之前,必须先打开文件,使用open()函数。

open(file, mode='r', buffering=-1, encoding=None)
  • file表示文件的路径(相对路径或绝对路径)
  • mode 文件打开模式,如果文件准备写操作,则mode应该写相对应字符,如'w'或'a'、'r+'、'w+'
    • w 只写文本
    • r 只读文本
    • rw 读写文本
    • r+ 读写文本, 如果文件不存在,则抛出异常
    • w+读写文本,如果文件不存在,则会自动创建; 另外,原内容则会被重写。
    • a 追加文件内容,在文件的末尾行新增内容, 原内容还存在。
    • r+b 读写字节数据
    • w+b 读写字节数据
    • ab 追加字节数据
  • buffering 指定读写数据的缓存大小,默认为-1,表示全部写入或全部读取。
  • encoding 表示读取文本数据的字符集,如果是b的字节模式,则不能设置encoding,否则抛出异常。
# 编写python的打印helloworld脚本,脚本名为a.py
stream = open('a.py', 'w', encoding='utf-8')  # 以只写的模式打开文件,返回一个文件流对象
stream.write("print('hello, world')")
stream.close()
stream = open('a.py', 'r', encoding='utf-8')
stream.read()
stream = open('a.py', 'w+', encoding='utf-8')
stream.write("print('hello, world')")  # 写入之后,文件句柄(指针)指向文件末尾的位置
stream.seek(0)  # 移动文件句柄到每一个字符的前面
stream.read()

文件流stream可以在with 中使用:

with open('a.py', 'w+', encoding='utf-8') as stream:
  stream.write("print('hello, world')")  # 写入之后,文件句柄(指针)指向文件末尾的位置
  stream.seek(0)  # 移动文件句柄到每一个字符的前面
  stream.read()

版权声明:
作者:Mr李
链接:https://www.techfm.club/p/52850.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>