深浅拷贝,生成器的创建方式,with语句和上下文管理器,property属性的基础介绍

property属性

1. property属性的介绍
property属性就是负责把一个方法当做属性进行使用,这样做可以简化代码使用。

定义property属性有两种方式
装饰器方式
类属性方式

2. 装饰器方式

‘’’

使用property类属性方式实现类中属性的简化操作
‘’’

class Account(object):
    def __init__(self,name,balace):
        self.__name = name
        self.__balance = balace
        self.__age = 0

    @property
    def username(self):
        print('username run ')
        return self.__name
'''
如果使用装饰器形式来简化属性操作时,
@property 只能用来装饰 get 方法

被装饰的属性方法前面不需要在使用 set 或 get 做为前缀了(方法名和属性名不一同要相同)

在装饰 set 方法时, 要保证set 方法的方法名和Get方法的方法名同名(同一属性)
先使用 @property装饰get方法, 装饰完成后,使用 @get方法名.setter 形式装饰set 方法
 '''

    @property
    def balance(self):
        print('get_balance run ...')
        return self.__balance

    @balance.setter
    def balance(self,money):
        print('set_balance run...')
        if isinstance(money,int):
            self.__balance += money


    @property
    def age(self):
        print('get age')
        return self.__age

    @age.setter
    def age(self,a):
        print('set age')
        self.__age += a




jackma = Account('JackMa',999999999)
print(jackma.username)
jackma.balance = 1
print(jackma.balance)
print(jackma.age)
jackma.age = 10
print(jackma.age)

代码说明:
@property 表示把方法当做属性使用, 表示当获取属性时会执行下面修饰的方法
@方法名.setter 表示把方法当做属性使用,表示当设置属性时会执行下面修饰的方法
装饰器方式的property属性修饰的方法名一定要一样。

3. 类属性方式

class Account(object):
    def __init__(self,name,balace):
        self.__name = name
        self.__balance = balace

    def get_name(self):
        return self.__name


    def get_balance(self):
        print('get_balance run ...')
        return self.__balance

    def set_balance(self,money):
        print('set_balance run...')
        if isinstance(money,int):
            self.__balance += money

    # 当使用property在类中定义了一个类属性时,这时可以通过这个类属性间接的操作 类中的对象属性,
	可以实现细节的隐藏和简化使用操作.
    username = property(get_name)
    balance = property(get_balance,set_balance)



jackma = Account('JackMa',999999999)
print(jackma.username)
print(jackma.balance)
# jackma.name = 'Guo'
print(jackma.username)
print(jackma.balance)

# print(jackma.get_name())
#
# jackma.set_balance(1000)
# print(jackma.get_balance())
运行结果:

0
100
成精了

代码说明:

property的参数说明:
    第一个参数是获取属性时要执行的方法
    第二个参数是设置属性时要执行的方法

4. 小结
定义property属性有两种方式:
装饰器方式
类属性方式
装饰器方式:
@property 修饰获取值的方法
@方法名.setter 修饰设置值的方法
类属性方式:
类属性 = property(获取值方法, 设置值方法)

with语句和上下文管理器

  1. with语句的使用
基础班向文件中写入数据的示例代码:

 # 1、以写的方式打开文件
 f = open("1.txt", "w")
 # 2、写入文件内容
 f.write("hello world")
 # 3、关闭文件
 f.close()

代码说明:

文件使用完后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的

这种写法可能出现一定的安全隐患,错误代码如下:


 # 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

Python提供了 with 语句的这种写法,既简单又安全,并且 with 语句执行完成以后自动调用关闭文件操作,
即使出现异常也会自动调用关闭文件操作。

with 语句的示例代码:

# 1、以写的方式打开文件
with open("1.txt", "w") as f:
# 2、读取文件内容
f.write("hello world")
  1. 上下文管理器

一个类只要实现了__enter__()和__exit__()这个两个方法,通过该类创建的对象我们就称之为上下文管理器。

自定义上下文管理器类,模拟文件操作:

定义一个File类,实现 enter() 和 exit()方法,然后使用 with 语句来完成操作文件, 示例代码:

class File(object):

    # 初始化方法
    def __init__(self, file_name, file_model):
        # 定义变量保存文件名和打开模式
        self.file_name = file_name
        self.file_model = file_model

    # 上文方法
    def __enter__(self):
        print("进入上文方法")
        # 返回文件资源
        self.file = open(self.file_name,self.file_model)
        return self.file

    # 下文方法
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("进入下文方法")
        self.file.close()


if __name__ == '__main__':

    # 使用with管理文件
    with File("1.txt", "r") as file:
        file_data = file.read()
        print(file_data)

运行结果:

进入上文方法
hello world
进入下文方法

代码说明:
__enter__表示上文方法,需要返回一个操作文件对象
__exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法。

小结 :
Python 提供了 with 语句用于简化资源释放的操作,
使用 with 语句操作建立在上下文管理器(实现__enter__和__exit__)的基础上

生成器的创建方式

  1. 生成器的介绍

根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。
数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存。

  1. 创建生成器的方式

    1. 生成器推导式
    2. yield 关键字

生成器推导式:

与列表推导式类似,只不过生成器推导式使用小括号
# 创建生成器
my_generator = (i * 2 for i in range(5))
print(my_generator)

# next获取生成器下一个值
# value = next(my_generator)
# print(value)

# 遍历生成器
for value in my_generator:
    print(value)

代码说明:
next 函数获取生成器中的下一个值
for 循环遍历生成器中的每一个值

yield 关键字:
只要在def函数里面看到有 yield 关键字那么就是生成器

def mygenerater(n):
    for i in range(n):
        print('开始生成...')
        yield i
        print('完成一次...')


if __name__ == '__main__':

    g = mygenerater(2)
    # 获取生成器中下一个值
    # result = next(g)
    # print(result)

    # while True:
    #     try:
    #         result = next(g)
    #         print(result)
    #     except StopIteration as e:
    #         break

    # # for遍历生成器, for 循环内部自动处理了停止迭代异常,使用起来更加方便
    for i in g:
        print(i)

代码说明:

代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
while 循环内部没有处理异常操作,需要手动添加处理异常操作
for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。

运行结果:
开始生成…
0
完成一次…
开始生成…
1
完成一次…

  1. 生成器的使用场景

数学中有个著名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

现在我们使用生成器来实现这个斐波那契数列,每次取值都通过算法来生成下一个数据, 生成器每次调用只生成一个数据,可以节省大量的内存。

def fibonacci(num):
    a = 0
    b = 1

    # 记录生成fibonacci数字的下标
    current_index = 0

    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
        yield result


fib = fibonacci(5)
# 遍历生成的数据
for value in fib:
    print(value)

运行结果:

0
1
1
2
3

  1. 小结

    生成器是根据算法生成数据的一种机制,每次调用生成器只生成一个值,可以节省大量内存。
    生成器的创建有两种方式:
    生成器推导式
    yield 关键字

深拷贝和浅拷贝

  1. 浅拷贝
    import copy # 使用浅拷贝需要导入copy模块

copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝,
对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象。

不可变类型的浅拷贝示例代码:

import copy  # 使用浅拷贝需要导入copy模块

# 不可变类型有: 数字、字符串、元组

a1 = 123123
b1 = copy.copy(a1)  # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))

print("-" * 10)
a2 = "abc"
b2 = copy.copy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))

print("-" * 10)
a3 = (1, 2, ["hello", "world"])
b3 = copy.copy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))

运行结果:

140459558944048
140459558944048
----------
140459558648776
140459558648776
----------
140459558073328
140459558073328

不可变类型的浅拷贝说明:

通过上面的执行结果可以得知,不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,
 而只是拷贝了这个对象的引用。

可变类型的浅拷贝示例代码:

import copy # 使用浅拷贝需要导入copy模块

# 可变类型有: 列表、字典、集合

a1 = [1, 2]
b1 = copy.copy(a1) # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = {"name": "张三", "age": 20}
b2 = copy.copy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = {1, 2, "王五"}
b3 = copy.copy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))

print("-" * 10)
a4 = [1, 2, [4, 5]]
# 注意:浅拷贝只会拷贝父对象,不会对子对象进行拷贝
b4 = copy.copy(a4) # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a4))
print(id(b4))
print("-" * 10)
# 查看内存地址
print(id(a4[2]))
print(id(b4[2]))

# 修改数据
a4[2][0] = 6

# 子对象的数据会受影响
print(a4)
print(b4)

运行结果:

139882899585608
139882899585800
----------
139882919626432
139882919626504
----------
139882919321672
139882899616264
----------
139882899587016
139882899586952
----------
139882899693640
139882899693640
[1, 2, [6, 5]]
[1, 2, [6, 5]]

可变类型的浅拷贝说明:

通过上面的执行结果可以得知,可变类型进行浅拷贝只对可变类型的第一层对象进行拷贝,
对拷贝的对象会开辟新的内存空间进行存储,子对象不进行拷贝。
  1. 深拷贝

deepcopy函数是深拷贝, 只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝,
对每一层拷贝的对象都会开辟新的内存空间进行存储。

不可变类型的深拷贝示例代码:

import copy  # 使用深拷贝需要导入copy模块

# 不可变类型有: 数字、字符串、元组

a1 = 1
b1 = copy.deepcopy(a1)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = "张三"
b2 = copy.deepcopy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = (1, 2)
b3 = copy.deepcopy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))
print("-" * 10)

# 注意: 元组里面要是有可变类型对象,发现对象有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝
a4 = (1, ["李四"])
b4 = copy.deepcopy(a4)
# 查看内存地址
print(id(a4))
print(id(b4))
# 元组里面的可变类型子对象也会进行拷贝
print(id(a4[1]))
print(id(b4[1]))

运行结果:

9289120
9289120
----------
140115621848320
140115621848320
----------
140115621859592
140115621859592
----------
140115602480584
140115621834568
140115602328136
140115602436168

不可变类型的深拷贝说明:

通过上面的执行结果可以得知:
    不可变类型进行深拷贝如果子对象没有可变类型则不会进行拷贝,而只是拷贝了这个对象的引用,否则会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储

可变类型的深拷贝示例代码:

import copy  # 使用深拷贝需要导入copy模块

# 可变类型有: 列表、字典、集合

a1 = [1, 2]
b1 = copy.deepcopy(a1)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = {"name": "张三"}
b2 = copy.deepcopy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = {1, 2}
b3 = copy.deepcopy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))
print("-" * 10)

a4 = [1, 2, ["李四", "王五"]]
b4 = copy.deepcopy(a4)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a4))
print(id(b4))

# 查看内存地址
print(id(a4[2]))
print(id(b4[2]))
a4[2][0] = "王五"
# 因为列表的内存地址不同,所以数据不会收到影响
print(a4)
print(b4)

运行结果:

140348291721736
140348291721928
----------
140348311762624
140348311221592
----------
140348311457864
140348291752456
----------
140348291723080
140348291723144
140348291723208
140348291723016
[1, 2, ['王五', '王五']]
[1, 2, ['李四', '王五']]

可变类型的深拷贝说明:

通过上面的执行结果可以得知, 可变类型进行深拷贝会对该对象到最后一个可变类型的每一层对象就行拷贝, 
 对每一层拷贝的对象都会开辟新的内存空间进行存储。
  1. 浅拷贝和深拷贝的区别
    浅拷贝最多拷贝对象的一层
    深拷贝可能拷贝对象的多层

    1. 浅拷贝使用copy.copy函数
    2. 深拷贝使用copy.deepcopy函数
    3. 不管是给对象进行深拷贝还是浅拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝的对象。
    4. 浅拷贝和深拷贝的区别是:
      4.1 浅拷贝最多拷贝对象的一层,深拷贝可能拷贝对象的多层。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值