Python笔记17-析构函数和封装

一、面向对象

1.内存中的对象

# 1
# 注意1:一般情况下,通过同一个类创建的对象,在内存中占用不同的地址
class Person():
    pass
p1 = Person()
print(id(p1))
p2 = Person()
print(id(p2))
print(p1 is p2)

# 2.
# 注意2:不同对象的重名的属性,在内存中占用不同的空间,当一个对象的属性值发生修改,对其他对象的属性值没有影响
class Person():
    __slots__ = ("name",)
    def __init__(self,name):
        self.name = name
p1 = Person('aaa')
print(p1.name)
p2 = Person('aaa')
print(p2.name)

# 思考问题:p1.name和p2.name是否是同一份内存地址?---->不是同一份内存空间
# print(p1.name is p2.name)  # True
# print(id(p1.name) == id(p2.name))  # True

p1.name = 'tom'
print(p1.name,p2.name)

"""
总结:
    如果两个对象的地址相同,则这两个对象的属性值一定相同
    如果两个对象的属性值相同,这两个对象不一定是同一个对象
"""

2.析构函数【了解】

构造函数:当对象创建的时候自动调用的函数
和构造函数相反,当对象被销毁的时候自动调用的而调用,称为析构函数,函数名为__del__

"""
构造函数:创建对象的过程中自动调用的函数,包括__new__和__init__
析构函数:对象被销毁的时候自动调用的函数,__del__
"""

"""
【面试题】析构函数调用的时机
"""

class Person():
    def __init__(self):
        print("构造函数被调用了~~~~")
    def __del__(self):
        print("析构函数被调用了~~~~")

# 1.当对象被定义为全局变量,整个程序执行完毕的时候,对象会被自动销毁,会自动调用析构函数
# print("start")
# p = Person()
# print("over")
"""
start
构造函数被调用了~~~~
over
析构函数被调用了~~~~
"""

print("*" * 30)

# # 2.当对象被定义为局部变量【函数中】,当函数执行完毕,对象会被自动销毁,会自动调用析构函数
# def func():
#     p = Person()
# print("start")
# func()
# print("over")
"""
start
构造函数被调用了~~~~
析构函数被调用了~~~~
over
"""

print("*" * 30)

# 3.del xx,当del执行完毕,对象会被立即销毁,,会自动调用析构函数
print("start")
p = Person()
del p
print("over")
"""
start
构造函数被调用了~~~~
析构函数被调用了~~~~
over
"""

# 析构函数的使用场景:很少用,但是可以用于程序的清理行为,如:关闭文件,关闭数据库等

3.类属性和实例属性

【面试题】类属性和实例属性之间的区别

a.定义的位置不同:类属性直接定义在类中,实例属性定义在构造函数中 或者 动态绑定

b.访问方式不同:类属性可以通过类名或者对象访问,实例属性只能通过对象访问

c.访问的优先级不同:当类属性和实例属性重名时,通过对象访问,优先访问的是实例属性

d.在内存中出现的时机不同:类属性优先于实例属性出现在内存中,类属性随着类的加载而出现,实例属性随着对象的创建才出现

e.使用场景不同:如果是所有对象共享的数据,则定义为类属性,如果是每个对象特有的数据,则定义为实例属性

class Person():
    # 类属性【类的字段】
    num = 66
    test = "abc"
    def __init__(self,name,age):
        # 实例属性【对象属性、对象的字段】
        self.name = name
        self.age = age
        self.test = "hello"

p = Person('aaa',10)
# 实例属性【对象属性、对象的字段】
p.score = 100

# 访问方式
# 实例属性:对象.属性
print(p.name,p.age,p.score)
# 类属性:对象.属性  或  类名.属性
print(p.num)
print(Person.num)

# 优先级
print(Person.test)   # abc
print(p.test)   # hello

del p.test
print(p.test)  # abc
print("*" * 40)

p1 = Person('bbb',20)
print(p.num)   # 66
print(p1.num)  # 66


# 修改类属性
# 注意1:对象.属性 = 值,该语法都表示动态绑定属性,此处的属性表示实例属性
# p.num = 88
# print(Person.num)   # 66
# 注意2:如果要修改类属性的值,则通过 类名.属性  = 值 修改
Person.num = 88
print(Person.num)

print(p.num)   #
print(p1.num)  #


# 使用场景不同:如果是所有对象共享的数据,则定义为类属性,如果是每个对象特有的数据,则定义为实例属性
class Student():
    school = "千锋"
    def __init__(self,name,score):
        self.name = name
        self.score = score
s1 = Student("张三",100)
s2 = Student("李四",88)
print(s1.school,s2.school)
Student.school = "万锋"
print(s1.school,s2.school)

s1.name = 'jack'
print(s2.name)

二、封装

1.概念

广义的封装:函数的定义和类的提取,都是封装的体现

狭义的封装:在面向对象编程中,一个类的某些属性,在使用的过程中,如果不希望被外界【直接】访问,就可以将该属性封装【将不希望被外界直接访问的属性私有化private,该属性只能被当前类持有,此时可以给外界暴露一个访问的函数即可】

封装的本质:就是属性私有化的过程

封装的好处:提高了数据的安全性,提高了数据的复用性

举例:插排,不需要关心属性在类的内部被做了什么样的操作,只需要关心可以将值传进去,也可以将值获取出来

2.私有属性

2.1基本使用

公开属性【public】:可以在类以外的任何地方直接访问的属性

私有属性【private】:只能在当前类的内部被直接访问的属性

# 1.属性未被私有化【公开属性】
# 特点:可以在类中及类以外的任何地方直接访问的属性
class Animal1():
    def __init__(self,name,age):
        self.name = name
        self.age = age
    # 在类的内部访问
    def show(self):
        print(f"姓名:{self.name},年龄:{self.age}")

a1 = Animal1('旺财',2)
# 在类的外面直接访问
# 获取值
print(a1.name,a1.age)
a1.show()
# 修改值
a1.name = "小白"
a1.age = 3
print(a1.name,a1.age)
a1.show()

print("*" * 50)

# 2.将属性私有化【私有属性】
# 特点:只能在当前类的内部被直接访问的属性
class Animal2():
    __slots__ = ("__name","__age")
    def __init__(self,name,age):
        # 在Python中,在属性名的前面添加两个下划线,则该属性被称为私有属性
        self.__name = name
        self.__age = age
    def show(self):
        print(f"姓名:{self.__name},年龄:{self.__age}")

a2 = Animal2('旺财',2)
# 获取值
a2.show()
# print(a2.name,a2.age)
# print(a2.__name,a2.__age)

"""
工作原理:
    一个属性一旦被私有化,会在内存中以另外一种形式存在,一般格式:_类名__属性名
    可能在不同的版本下,不同的操作系统下,私有属性可能以不同的形式存在,无法访问
    
    既然属性被私有化了,就不希望被外界访问
"""
# 不建议使用
# print(a2._Animal2__name)
# print(a2._Animal2__age)

print("*" * 30)

# 3.提供给外界访问的函数【少用较少】
# a.
class Animal3():
    __slots__ = ("__name","__age")
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def show(self):
        print(f"姓名:{self.__name},年龄:{self.__age}")

    # 获取值
    def get_name(self):
        return self.__name
    # 修改值
    def set_name(self,name):
        self.__name = name

    def get_age(self):
        return self.__age
    def set_age(self,age):
        if age < 0:
            age = -age
        self.__age = age

a3 = Animal3('旺财',2)
print(a3.get_name())       # a3.name
a3.set_name("小白")         # a3.name = "小白"
print(a3.get_name())

print(a3.get_age())
a3.set_age(-18)
print(a3.get_age())

print("*" * 30)


# b.在Python中,通过装饰器给类的外界提供访问的函数
# @property:将被装饰的函数可以当做属性使用,作用是获取值,格式:对象.函数名 表示获取该函数的返回值
# @xxx.setter:将被装饰的函数可以当做属性使用,作用是给属性赋值,格式:对象.函数名 = 值
class Animal3():
    __slots__ = ("__name","__age")
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def show(self):
        print(f"姓名:{self.__name},年龄:{self.__age}")

    # 被@property装饰的函数用于获取属性值,相当于get_age()
    @property
    def name(self):
        return self.__name
    # 被@xxx.setter装饰的函数用于给属性设置值,相当于set_age(),xxx表示被@property装饰的函数名
    @name.setter
    def name(self,name):
        self.__name = name

    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self,age):
        if age < 0:
            age = -age
        self.__age = age

a3 = Animal3('旺财',2)
print(a3.name)   # 调用被@property装饰的函数,获取该函数的返回
a3.name = "花花"  # 调用被@name.setter装饰的函数,给属性赋值
print(a3.name)

print(a3.age)
a3.age = 4
print(a3.age)

# 注意:@property可以单独使用,可以装饰任意函数,@xxx.setter不能单独使用

2.2不同形式的属性

"""
【面试题】解释下面不同形式的变量出现在类中的意义
a:普通属性,也被称为公开属性,在类的外面可以直接访问             ****
_a:在类的外面可以直接访问,但是不建议使用,容易和私有属性混淆
__a:私有属性,只能在类的内部被直接访问。类的外面可以通过暴露给外界的函数访问    *****
__a__:在类的外面可以直接访问,但是不建议使用,因为系统属性和魔术方法都是这种形式的命名,
    如:__slots__  __init__  __new__  __del__,__name__,__add__,__sub__,__mul__等
"""
class Person():
    def __init__(self,name,age,weight,score):
        self.name = name
        self._age = age
        self.__weight = weight
        self.__score__ = score
p = Person("小明",10,150,80)
print(p.name)
print(p._age)
print(p.__score__)

3.私有函数

# 1.
# class Person():
#     # 公开函数
#     def func1(self):
#         print("func~~~111")
# p = Person()
# p.func1()


# 2.
class Person():
    # 公开函数
    def func1(self):
        print("func~~~222")

        self.__show()

    # 私有函数
    def __show(self):
        print("showing")

p = Person()
p.func1()
# p.__show()

"""
注意:
    a.如果一个函数需要被私有化,则可以在类中定义一个公开函数,在公开函数中调用私有函数即可
    b.类中的实例函数之间相互调用,语法:self.函数名(形参)
    c.如果封装的功能只希望在类的内部使用,类的外部不希望被访问
"""

封装的作用:

a.提高了数据的安全性

b.提高了数据的复用性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值