文章目录
一、变量
1、概念理解
- 变量是会变的量。它是一个装数据的容器,里面的数据是可以变的,因此叫变量
2、变量定义
- 在 Python 中,每个变量 在使用前都必须赋值,变量 赋值以后 该变量 才会被创建
- 等号(=)用来给变量赋值
=
左边是一个变量名=
右边是存储在变量中的值
变量名 = 值 #变量定义之后,后续就可以直接使用了
3、变量的类型
在内存中创建一个变量,会包括:
- 变量的名称
- 变量保存的数据
- 变量存储数据的类型
- 变量的地址(标示)
案例 1:定义变量
# 定义 tmooc 账号变量
tmooc_account = "1234567@qq.com" #一句话类型
# 定义 tmooc 密码变量
tmooc_password = 123456 #整数类型
# 在程序中,如果要输出变量的内容,需要使用 print 函数
print(tmooc_account)
print(tmooc_password)
练习 1:买包子
- 可以用 其他变量的计算结果 来定义变量
- 变量定义之后,后续就可以直接使用了
需求
- 包子的价格是 1.5 元/个
- 买了 10 个 包子
- 计算付款金额
# 定义包子价格变量
price = 1.5
# 定义购买数量
number = 10
# 计算金额
money = price * number
print(money)
练习 2:买包子进阶
- 今天老板高兴,总价打 9 折
- 请重新计算购买金额
# 定义包子价格变量
price = 1.5
# 定义购买数量
number = 10
# 计算金额
money = price * number
# 总价打 98 折
money = money * 0.9
print(money)
提问
- 上述代码中,一共定义有几个变量?
- 三个:
price
/number
/money
- 三个:
money = money * 0.9
是在定义新的变量还是在使用变量?- 直接使用之前已经定义的变量
- 变量名 只有在 第一次出现 才是 定义变量
- 变量名 再次出现,不是定义变量,而是直接使用之前定义过的变量
- 在程序开发中,可以修改之前定义变量中保存的值吗?
- 可以
- 变量中存储的值,就是可以 变 的
二、变量作用域
1. 全局变量
- 标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性
- 在一个模块中最高级别的变量有全局作用域
- 全局变量的一个特征是除非被删除掉,否则他们会存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的
全局变量的使用
>>> x = 10 # 定义全局变量x
>>> def func1(): # 定义函数func1(),函数内部可以直接使用变量x
... print(x)
...
>>> func1() #调用函数func1(),结果为10
2. 局部变量
- 局部变量只是暂时的存在,仅仅只依赖于定义他们的函数现阶段是否处于活动
- 当一个函数调用出现时,其局部变量就进入声明它们的作用域。在那一刻,一个新的局部变量名为那个对象创建了
- 一旦函数完成,框架被释放,变量将会离开作用域
局部变量只在函数内部起作用
>>> def func2(): #定义函数func2(), 其中的变量a为局部变量,只在函数内部有效
... a = 10
... print(a)
...
>>> def func3(): #定义函数func2(), 其中的变量a为局部变量,只在函数内部有效
... a = 'hello'
... print(a)
...
>>> func2() #调用函数func2(),结果为10
>>> func3() #调用函数func3(), 结果为hello
>>> a #查看a的值,没有被定义,函数内部的a为局部变量,只在该函数内部有效
如果局部变量与全局变量有相同的名称,那么函数运行时,局部变量的名称将会把全局变量的名称遮盖住
>>> x = 100 # 定义全局变量x
>>> def func5(): # 声明函数func5(), 函数内有局部变量x=200
... x = 200
... print(x)
...
>>> func5() # 局部变量
200
>>> x # 查看x【全局变量】,没有发生变化
100
3. global 语句
- 因为全局变量的名字能被局部变量给遮盖掉
- 为了明确地引用一个已命名的全局变量,必须使用 global 语句
>>> x = 100 #定义全局变量x
>>> def func6(): #定义函数func6()
... global x #引用全局变量x
... x = 200 #为全局变量x赋值为200
... print(x) #打印变量x的值
...
>>> func6() #调用函数func6()
>>> x
4. 查找变量或函数的顺序
- 首先在函数的内部去查找
- 函数内部没有,然后去全局去查找,看是否定义
- 全局也没有,最后会去内建函数中查找
# 验证python查找变量或函数的顺序,定义函数func7(),统计字符'abcd'的长度
>>> def func7():
... print(len('abcd'))
...
>>> func7() #调用函数,结果为4,正确
>>> len #全局查看是否有len,没有,不存在
# 先在函数func7()内部查找方法len(),再在全局查找,最后在内建中查找len()
5. 生成器
Python 使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
Python有两种不同的方式提供生成器:
-
生成器函数:
- 常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果
- yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
-
生成器表达式:
- 类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
>>> from random import randint >>> nums = [randint(1, 100) for i in range(10)] #产生10个1~99的随机数 >>> nums >>> nums2 = (randint(1, 100) for i in range(10)) #() 会产生一个随机数的生成器对象 >>> nums2 #为一个对象,不占用空间,使用时才会产生数据 >>> for i in nums2: #使用for循环遍历nums2中的元素,成功 ... print(i) ... >>> ['192.168.1.%s' %i for i in range(1, 255)] #使用列表解析产生一个254个元素的大列表,占据比较大的内存空间 >>> ips = ('192.168.1.%s' %i for i in range(1, 255)) #() 会产生一个IP地址的生成器对象【包含254个IP地址】 >>> ips #为一个对象,不占用空间,使用时才会产生数据 >>> for ip in ips: #使用for循环可以遍历出ips中的所有元素,成功 ... print(ip)
- Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
>>> sum((x ** 2 for x in range(4)) # 而不用多此一举的先构造一个列表: >>> sum([x ** 2 for x in range(4)])
- 生成器的好处在于延迟操作,需要的时候再使用,所以会节省空间
sum([i for i in range(100000000)]) sum((i for i in range(100000000)))
-
注意事项
- 生成器的唯一注意事项就是:生成器只能遍历一次
- 我们直接来看例子,假设文件中保存了每个省份的人口总数,现在,需要求每个省份的人口占全国总人口的比例。显然,我们需要先求出全国的总人口,然后在遍历每个省份的人口,用每个省的人口数除以总人口数,就得到了每个省份的人口占全国人口的比例。
- 自定义生成器函数的过程
- 在函数内部,有很多 yield 返回中间结果;
- 程序向函数取值时,当函数执行到第1个yield时,会暂停函数运行并返回中间结果;
- 当主程序再次调用函数时,函数会从上次暂停的位置继续运行,当遇到第2个yield,会再次暂停运行函数并返回数据;
- 重复以上操作,一直到函数内部的yield全部执行完成为止
练习 :文件生成器
需求:通过生成器完成以下功能
- 使用函数实现生成器
- 函数接受一个文件对象作为参数
- 生成器函数每次返回文件的 10 行数据
#定义生成器函数gen_block(),功能:每次取10行记录
def gen_block(fobj):
lines = [] #每次存储10行记录
counter = 0 #计数器,用于每次从文件中取10行记录
for line in fobj: #从文件对象中,一行一行读取记录
lines.append(line) #将读取的行记录,存储在列表lines中
counter += 1 #统计读取了几行记录
if counter == 10: #当读取10行记录时,执行代码块
yield lines #暂停运行函数,给函数返回列表lines
lines = [] #再次读取时,继续运行函数,清空lines
counter = 0 #再次读取时,counter复位到0,重新计数
if lines: #当lines不为空时,返回剩余的行记录【不足10行】
yield lines
if __name__ == '__main__':
fname = '/etc/passwd' #读取数据的文件
fobj = open(fname) #打开文件,默认只读数据
for block in gen_block(fobj): #调用生成器函数,每次打印10行记录
print(block) #打印每次读取的记录
print('*' * 50) #打印50个*, 区分每次读取的记录
fobj.close() #打开文件,就需要关闭文件