目录
property
- 装饰器方式
- 类属性方式
装饰器方式
class Person:
def __init__(self):
self.__age=15
@property
def age(self):
return self.__age
@age.setter
def age(self,newage):
self.__age=newage
p=Person()
print(p.age)
p.age=20
print(p.age)
# 15
# 20
代码说明:
- @property 表示把方法当做属性使用, 表示当获取属性时会执行下面修饰的方法
- @方法名.setter 表示把方法当做属性使用,表示当设置属性时会执行下面修饰的方法
- 装饰器方式的property属性修饰的方法名一定要一样。
类属性方式
class Person:
def __init__(self):
self.__age = 15
def get_age(self):
"""当获取age属性的时候会执行该方法"""
return self.__age
def set_age(self, newage):
self.__age = newage
age = property(get_age, set_age)
p = Person()
print(p.age)
p.age = 20
print(p.age)
#15
# 20
property的参数说明:
- 第一个参数是获取属性时要执行的方法
- 第二个参数是设置属性时要执行的方法
with语句的使用
# 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、读取文件内容
f.write("hello world")
# 3、关闭文件
f.close()
运行结果:
Traceback (most recent call last):
File "/home/python/Desktop/test/xxf.py", line 4, in <module>
f.write("hello world")
io.UnsupportedOperation: not writable
代码说明:
- 由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。
- 为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来解决
安全写法, 代码如下:
try:
# 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、读取文件内容
f.write("xxxxx")
except IOError as e:
print("文件操作出错", e)
finally:
# 3、关闭文件
f.close()
运行结果:
文件操作出错 not writable
这种方法虽然代码运行良好,但是缺点就是代码过于冗长,并且需要添加try-except-finally语句,不是很方便,也容易忘记.
在这种情况下,Python提供了 with 语句的这种写法,既简单又安全,并且 with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。
with 语句的示例代码:
# 1、以写的方式打开文件
with open("1.txt", "w") as f:
# 2、读取文件内容
f.write("hello world")
上下文管理器
类中的使用
一个类只要实现了__enter__()和__exit__()
这个两个方法,通过该类创建的对象我们就称之为上下文管理器。
with语句之所以这么强大,背后是由上下文管理器做支撑的
open 函数创建的文件对象就是就是一个上下文管理器对象
注意 普通函数和普通类是不能结合with语句使用的,该函数和普通类需要有上下文管理器做支撑才行
class File:
def __init__(self,file_name,file_mode):
self.file_name=file_name
self.file_mode=file_mode
def __enter__(self):
print('进入上文方法,返回一个资源对象如文件操作对象 数据库对象')
self.file= open(self.file_name,self.file_mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print('进入下文方法,不论上文方法对象执行有无错误,都执行该对象的释放资源操作')
self.file.close()
if __name__ == '__main__':
with File('1.txt','w') as f:
print(f.read())
'''
D:\python\python.exe C:/Users/Administrator/Desktop/pd/闭包.py
进入上文方法,返回一个资源对象如文件操作对象 数据库对象
进入下文方法,不论上文方法对象执行有无错误,都执行该对象的释放资源操作
Traceback (most recent call last):
File "C:\Users\Administrator\Desktop\pd\闭包.py", line 15, in <module>
print(f.read())
io.UnsupportedOperation: not readable
'''
函数的使用
from contextlib import contextmanager
@contextmanager
def file(file_name, file_mode):
try:
f = open(file_name, file_mode)
yield f
except Exception as e:
print(e)
finally:
print('over')
f.close()
with file('1.txt', 'w') as f:
print(f.read())
'''
not readable
over
'''
使用@contextmanager装饰器装饰函数,使其成为上下文管理函数,
此时yield f 之前视为上文方法 之后视为下文方法
此外此装饰器装饰的函数在程序出错的情况下不能自动执行下文方法,需要结合try系列去捕捉
生成器
- 生成器推导式
- yield 关键字
生成器与遍历和列表推导式十分相似,
区别在于遍历列表推导式是一次性取出所有数据加载到内存
而生成器则是next方式去取,一个个的加载内存 不管数据有多少,都是占据一个数据的内存
适合大数据中取出指定的或者全部数据,提高性能
生成器推导式
my_generator=(i for i in range(5))
# 生成的并非是元组推导式 而是生成器对象
print(my_generator)#<generator object <genexpr> at 0x000002459C324B30>
# 生成器对象需要使用next()一次一次的去取值
value=next(my_generator)
print(value)
value=next(my_generator)
print(value)
# 使用while遍历
# while True:
# # 取到最后一个值后再去数据就抛出异常StopIteration
# try:
# print(next(my_generator))
# except:
# break
# 推荐使用for
for i in my_generator:
print(i)
#由此看出for循环内部循环调用next函数获取生成器中的下一值,
# 当出现异常for循环内部自动进行了异常捕获
yield 关键字
只要在函数里面看到有yield那么这个函数可以任务是一个生成器
def my_generator():
for i in range(3):
print('开始生成数据')
yield i
print('上一次的数据取完了,又开始生成下一个数据啦')
result=my_generator()
print(result)#<generator object my_generator at 0x0000028820374B30>
for i in my_generator():
print(i)
'''
<generator object my_generator at 0x0000027977AB4B30>
开始生成数据
0
上一次的数据取完了,又开始生成下一个数据啦
开始生成数据
1
上一次的数据取完了,又开始生成下一个数据啦
开始生成数据
2
上一次的数据取完了,又开始生成下一个数据啦
'''
# 看到以上信息 总结出yield关键字理解成分割点,
# 生成器的本意就是一次次的取,当执行到yield就是取一次了,此时下面的程序不走
# 当下次再取数据的时候再去向下执行,执行到底回头再执行
- 代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
- 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
- while 循环内部没有处理异常操作,需要手动添加处理异常操作
- for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。
深拷贝和浅拷贝
拷贝只针对可变类型对象进行拷贝,拷贝就是对目标进行复制,粘贴给指定对象,内存地址值不同,也就是说会开辟新的空间 类似window的不同版本的相同软件
若是不可变类型进行拷贝,只是对其内存地址id()进行拷贝,也就是说拷贝后两者使用的是同个内存地址值,不会开启新的空间 类似window的快捷方式
可变类型:列表 字典 集合
不可变类型 数字 字符串 元组
所谓不可变 就是不可改变 意思就是 a=1 a=2 分别打印id(a) 不一致 就是改变a的值 其实是开启新的空间了 并非真的改变
可变 可以改变 就是 a=[1,2,3] a.append(2) 分别打印id(a) 一致 不会开启新空间
浅拷贝 就是 只会拷贝一层
深拷贝 拷贝一层或多层 元组中又含有列表(1,2,[2,3])这种只会对中的列表开辟新的内存空间
不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用
import copy
# 浅拷贝: 只会对可变类型的第一层对象进行拷贝,不会对子对象进行拷贝,
# 拷贝成功后会开辟一个新的内存空间存储拷贝这个对象
# 不可变类型: 数字、字符串、元组
num1 = 1
# copy() 表示是一个浅拷贝函数
# 浅拷贝
num2 = copy.copy(num1)
# 查看后内存地址没有发生变化,说明没有对对象进行拷贝,也就说没有开辟新的内存空间
print("num1:", id(num1), "num2:", id(num2))
# 对于不可变类型进行浅拷贝实际上是对引用的一个拷贝,两个变量指定的是一个内存地址
my_tuple1 = (3, 5)
my_tuple2 = copy.copy(my_tuple1)
print("my_tuple1:", id(my_tuple1), "my_tuple2:", id(my_tuple2))
# 得知结论: 浅拷贝不会对不可变类型进行拷贝,也就说不会开辟内存内存空间,
# 对于不可变类型进行浅拷贝实际上是对引用的一个拷贝,两个变量指定的是一个内存地址
# 可变类型: 列表、字典、集合
my_list1 = [1, 3, [4, 6]]
my_list2 = copy.copy(my_list1)
print("my_list1:", id(my_list1), "my_list2:", id(my_list2))
my_list1.append(5)
print(my_list1, my_list2)
print("my_list1[2]:", id(my_list1[2]), "my_list2[2]:", id(my_list2[2]))
my_list1[2].append(3)
print(my_list1, my_list2)
'''
num1: 3169229275376 num2: 3169229275376
my_tuple1: 3169230597184 my_tuple2: 3169230597184
my_list1: 3169233782464 my_list2: 3169233730752
[1, 3, [4, 6], 5] [1, 3, [4, 6]]
my_list1[2]: 3169233729984 my_list2[2]: 3169233729984
[1, 3, [4, 6, 3], 5] [1, 3, [4, 6, 3]]
'''
import copy
# 深拷贝: 只要发现对象有可变类型那么就是对该对象到最后一个可变类型的每一层对象进行拷贝,拷贝成功后会开辟新的内存空间
# 不可变类型: 数字、字符串、元组
num1 = 1
num2 = copy.deepcopy(num1)
print("num1:", id(num1), "num2:", id(num2))
str1 = 'hello'
str2 = copy.deepcopy(str1)
print("str1:", id(str1), "str2:", id(str2))
my_tuple1 = (1, [1,2])
my_tuple2 = copy.deepcopy(my_tuple1)
print("my_tuple1:", id(my_tuple1), "my_tuple2:", id(my_tuple2))
print("my_tuple1[1]:", id(my_tuple1[1]), "my_tuple2[1]:", id(my_tuple2[1]))
my_tuple2[1].append(4)
print(my_tuple1, my_tuple2)
print("my_tuple1[0]:", id(my_tuple1[0]), "my_tuple2[0]:", id(my_tuple2[0]))
# 如果发现元组里面有可变类型那么,会对元组进行拷贝和子元素列表进行拷贝,拷贝后都会产生一个新的内存空间
# 但是不可变类型不会进行拷贝,因为不可变类型不允许在原有内存空间的基础修改数据,
# 所以拷贝没有意义,因为每次修改数据内存地址都会发生变化
# 可变类型: 列表,字典,结合 , 对应深拷贝来说也会进行拷贝如果发现子对象也是可变类型也会进行拷贝,
# 拷贝后会开辟新的内存空间存储拷贝后的对象
my_list1 = [1,[2, 3]]
my_list2 = copy.deepcopy(my_list1)
print("my_list1:", id(my_list1), "my_list2:", id(my_list2))
print("my_list1[1]:", id(my_list1[1]), "my_list2[1]:", id(my_list2[1]))
# 无论是浅拷贝还是深拷贝都是针对的可变类型
'''
num1: 1925692981488 num2: 1925692981488
str1: 1925697777072 str2: 1925697777072
my_tuple1: 1925700909760 my_tuple2: 1925700909696
my_tuple1[1]: 1925700909504 my_tuple2[1]: 1925700910272
(1, [1, 2]) (1, [1, 2, 4])
my_tuple1[0]: 1925692981488 my_tuple2[0]: 1925692981488
my_list1: 1925700910976 my_list2: 1925700909376
my_list1[1]: 1925700961984 my_list2[1]: 1925700960832
'''