偷学Python第二十天:面向对象编程(四)面向对象的进阶

人生苦短我用Python

偷学Python第二十天:面向对象编程(四)面向对象的进阶

目标:可以用Python做一些简单的数据分析。
在这里插入图片描述

坚持完成自己的目标!勿忘初心~

今日学习目标

Python对象的引用以及Python类的装饰器

今日学习内容

引用的概念

引用 *(Reference)*是对象的指针

  • 引用是内存中真实对象的指针,表示为变量名或者内存地址
  • 每个对象存在至少一个引用,id()函数用于获得引用
  • 在传递参数和赋值时,Python传递对象的引用,而不是复制对象

示例代码

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
list1 = [1, 2, 3, 4]
list2 = list1
print(id(list1))  # 2044656837192
print(id(list2))  # 2044656837192
# 因为list1是类的实例化,list2引用的是list1,两个都是引用的最基础的object类,所以两个的结果是一样的

Python内部机制对引用的处理

  • 不可变对象:immutable解释器为相同值维护尽量少的内存区域
  • 可变对象:mutable解释器为每个对象维护不同内存区域

示例代码1

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
text1 = "甜甜很可爱"
text2 = text1
text3 = "甜甜很可爱"
text4 = "甜甜"
text5 = "很可爱"
text6 = text4 + text5
print(id(text1))  # 1616972638288
print(id(text2))  # 1616972638288
print(id(text3))  # 1616972638288
print(id(text4))  # 1616973621272
print(id(text5))  # 1616973578032
print(id(text6))  # 1616974246288


因为text1和2是引用的一个字符串,所以内存地址是一样的;因为Python解释器会大可能的节省内存空间,所以当不可变类型的值一样时,Python会将其自动的引用一个地址空间,来达到节省空间的目的,所以text1/2/3的地址空间是一致的;Python解释器并不会对计算出来的结果来进行地址空间的优化,就算两个的值是一样的,Python解释器也会为新计算出来的结果来开辟一个新的地址空间

扫描二维码关注公众号,回复: 11281505 查看本文章

示例代码2

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
list1 = []
list2 = []
list3 = []
print(id(list1))  # 3204114440776
print(id(list2))  # 3204114440840
print(id(list3))  # 3204115873544

每个可变对象都有自己独立的地址空间,并不复用地址空间

导致引用被+1的情况一般都4种

  1. 对象被创建
  2. 对象被引用
  3. 对象被作为函数或方法的参数
  4. 对象被作为一个容器中的元素

导致引用-1的情况一把也都4种

  1. 对象被删除
  2. 对象的名字呗赋予新的对象
  3. 对象离开作用域
  4. 对象所在容器被删除

对象的拷贝

拷贝是复制一个对象为新对象,内存空间有”变化“,拷贝分为浅拷贝和深拷贝

  • 浅拷贝:仅仅复制最顶层对象的拷贝方式,默认拷贝方式
  • 深拷贝:迭代复制所有对象的拷贝方式

示例代码(浅拷贝1)

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
list1 = ["甜甜", [1, 2, 3]]
list2 = list1.copy()  # 使用copy方法复制
list3 = list1[:]  # 使用切片复制
list4 = list(list1)  # 使用生成列表方式复制
for ch in [list1, list2, list3, list4]:
    for i in ch:
        print(i, id(i),  "\t", end="")  # 打印列表的没一项和id
    print(ch, id(ch))  # 打印每个列表和id
    
'''
---输出结果---
甜甜 2905817180184 	[1, 2, 3] 2905787490888 	['甜甜', [1, 2, 3]] 2905787490952
甜甜 2905817180184 	[1, 2, 3] 2905787490888 	['甜甜', [1, 2, 3]] 2905817092488
甜甜 2905817180184 	[1, 2, 3] 2905787490888 	['甜甜', [1, 2, 3]] 2905817137800
甜甜 2905817180184 	[1, 2, 3] 2905787490888 	['甜甜', [1, 2, 3]] 2905817771656
'''

浅拷贝只是拷贝的列表这一层的内存空间,里面的元素的内存空间不会被拷贝

示例代码(浅拷贝2)

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
list1 = ["甜甜", [1, 2, 3]]
list2 = list1.copy()  # 使用copy方法复制
list3 = list1[:]  # 使用切片复制
list4 = list(list1)  # 使用生成列表方式复制
list4[1].append(4)
print(list1)
print(list2)
print(list3)
print(list4)
'''
--输出结果--
['甜甜', [1, 2, 3, 4]]
['甜甜', [1, 2, 3, 4]]
['甜甜', [1, 2, 3, 4]]
['甜甜', [1, 2, 3, 4]]
'''

这里只对list4进行来数据的修改,但是所有的列表的内容都发生了;这是因为每个列表所引用的内容是一样的,所以修改了1个四个会发生改变

深拷贝要采用copy库里面的deepcopy()方法,迭代拷贝对象内层的各层次对象,完全新开辟内存空间建立对象以及对象下层的各种对象元素,深拷贝仅仅针对可变类别,不可变类型不许创建新对象

示例代码

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""
import copy  # 导入库
list1 = ["甜甜", [1, 2, 3]]
list2 = copy.deepcopy(list1)  # 使用copy库的deepcopy方法复制
for ch in [list1, list2]:
    for i in ch:
        print(i, id(i),  "\t", end="")  # 打印列表的没一项和id
    print(ch, id(ch))  # 打印每个列表和id

'''
---输出结果---
甜甜 2190823984184 	[1, 2, 3] 2190853845832 	['甜甜', [1, 2, 3]] 2190853766728
甜甜 2190823984184 	[1, 2, 3] 2190853961544 	['甜甜', [1, 2, 3]] 2190853961480
'''

因为“甜甜”字符串属于不可变类型,所以其地址空间不会发生改变,剩下的地址空间都发生了改变

实例方法的引用

实例方法也是一种引用,就是对象本身的引用,当方法被引用时,方法(即函数)将产生一个对象:方法对象

类的特性装饰器

@property装饰器可以把方法改变成对外可见的”属性“,在类内部表现为方法,在外边表现为属性

示例代码

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""


class TestClass:
    def __init__(self, name):
        self.name = name

    @property    # 将方法转换为属性
    def age(self):
        return self.__age

    @age.setter  # 为属性进行赋值操作
    def age(self, value):
        if value < 0 or value > 110:
            value = 19
        self.__age = value


tt = TestClass("甜甜")
bb = TestClass("贝贝")
tt.age = 18
bb.age = -19
print(tt.age)  # 18
print(bb.age)  # 19

类的名称修饰

名称修饰 (Name Mangling)是类中名称的转换约定,Python可以通过名称修饰来完成一些重要功能,在Python中采用下划线_来进行名称修饰,分为5种情况,

_name

name_

__name

__name__

_

_单下划线开头的名称修饰

  • 单下划线开头属性或者方法为类内部使用的约定,是PEP8规定的一种约定
  • 只是约定,依然可以通过<对象名>.<属性名>方式访问
  • 在功能的上的不同是使用from XX import *时不会导入单下划线开头的属性或者方法

示例代码

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""


class TestClass:
    def __init__(self, name):
        self._name = name  # 约定在内部使用


tt = TestClass("甜甜")
print(tt._name)  # 甜甜

虽然约定在内部使用,但是依然可以被访问

_单下划线结尾的名称修饰

单下划线结尾的属性或者方法是避免与保留字或已有命名冲突,这也是PEP8规定的,这仅仅是一个约定,没有任何对应的功能

__双下划线开头的名称修饰

双下划线开头属性或者方法将被解释器修改名称,避免命名冲突,这不是一个约定,而是功能性的,__nama会被修改为_<类名>__name的形式,来实现私有属性、私有方法;这是一种类的名称修饰,间接的来当做私有属性或者私有方法

__name__双下划线开头和结尾的名称修饰

双下划线开头和结尾的属性或方法没有任何特殊功能,名字不能被修改,部分名称是保留属性或者保留方法

单下划线

单下划线仅仅是一个无关紧要的名字吗,没有特殊功能

Python的最小空类

作用

  • 类是一个命名空间,最小空类可以当做命名空间使用
  • 最小空类可以辅助存储和使用
  • 动态增加属性是Python类的一个特点

示例代码

"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/18
"""


class TestClass:
    pass


a = TestClass
a.text = "甜甜很可爱"
print(a.text)  # 甜甜很可爱
# 可以动态增加属性来达到存储信息的目的

今日学习总结

在这里插入图片描述

明日学习计划

用面向对象的思维来完成一些案例

甜甜有话说

用了5天终于把理论支持完成了,在拿出两天的时间练习练习用面向对象的思维来编写程序

座右铭:不要在该奋斗的年纪选择安逸!!

加油

古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼

猜你喜欢

转载自blog.csdn.net/weixin_46163658/article/details/106201479