面向对象进阶2

类的封装

封装是什么

封装就是把东西放在一起然后封起来,别人就拿不到了

封装的两个层面

第一个层面

  • 第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

第二个层面

  • 第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

在python中用双下划线的方式实现隐藏属性(设置成私有的)

类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

class A:
    __count = 0
    
    def __init__(self,name):
        self.name = name
        
    def __f1(self):
        print('from A.__f1')
        A.__count += 1
        print(A.__count)
        
        
    def bar(self):
        self.__f1()
        
a = A('hades')
print(a.__count)  # 报错,拿不到__count,这是封装在类内部,外部不能调用
        
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-59-3b81c13cf856> in <module>
     15 
     16 a = A('hades')
---> 17 print(a.__count)  # 报错,拿不到__count,这是封装在类内部,外部不能调用
     18 


AttributeError: 'A' object has no attribute '__count'
a.bar()  # 这是在类内部进行操作,才可以拿到
from A.__f1
1
  • 注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

这种变形需要注意的是:

  1. 这种机制并没有真正意义上的限制我们从外部直接访问属性,我们可以通过实例化对象名._类名__属性名进行访问
a._A__count += 10  # 此时的_A__count是对象a的属性,相当于a._A_count = a._A__count + 10
                   # 此时等号后面的a._A__count会先去a这个对象进行查找_A__count,找不到就去类里面找,找到了就进行运算
print(a.__dict__)
print(a._A__count)
{'name': 'hades', '_A__count': 11}
11
a._A__f1()   # 这个访问的是A._A__count
from A.__f1
2
  1. 变形的过程只在类的定义的时候发生一次,在定义后的赋值操作,不会变形

私有模块

同样的,我们模块也是有私有属性的,也是通过加下划线的方式进行封装,但只是一个下划线

# m1.py

_X = 10

y = 20

# m2.py

from m1 import *

print(y)  # 20

print(_X) 进行报错
from m1 import _X

print(_X) # 10

类的property特性

什么是property特性

  • property装饰器用于将被装饰的方法伪装成一个数据类型,在使用的时候可以不加括号而直接使用

注意:这种只适合类内部除了self参数外没有其他参数的函数

class Foo:
    def func(self):
        print('from func')
       
    @property
    def pro(self):
        print('from pro')
        
        
f = Foo()
f.func()  # 正常调用类的方法
f.pro  # 将类的方法当成属性使用
from func
from pro

property属性的定义和调用要注意一下几点:

  1. 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数

  2. 调用时,无需括号

property属性的两种方式

  1. 装饰器:在方法上应用装饰器
  2. 类属性:在类中定义值为property对象的类属性

装饰器

  1. 经典类只具有@property这一种方法

  2. 新式类中具有@property,@方法名.setter,@方法名.deleter这三种方法

class Goods:
    def __init__(self, name):
        self.name = name
        self.goods_price = 125

    @property
    def price(self):
        print(self.goods_price)

    @price.setter
    def price(self, value):
        self.goods_price = value
        print('from good_price')
        return self.goods_price

    @price.deleter
    def price(self):
        del self.goods_price


obj = Goods('clothes')
print(obj.__dict__)
obj.price  # 自动调用@property装饰的函数

obj.price = 250 # 自动调用@price.setter装饰的函数
obj.price

del obj.price # 自动调用@price.deleter装饰的函数
print(obj.__dict__)
{'name': 'clothes', 'goods_price': 125}
125
from good_price
250
{'name': 'clothes'}

类与对象的绑定方法和非绑定方法

绑定方法

对象的绑定方法

在类中,没有被任何装饰器修饰的方法就是 绑定到对象的方法,这类方法专门为对象定制

class People:
    country = 'china'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def discribe(self):
        print(f'{self.name} is {self.age} years old')
        
p = People('hades',27)
p.discribe()   # 通过对象进行调用
hades is 27 years old

discribe即为对象的绑定方法,这个方法不在对象的名称空间,而是在类的名称空间中

  • 通过对象调用绑定到对象的方法,会有一个自动传值的过程,即自动将当前对象传递给方法的第一个参数(self,一般都叫self,也可以写成别的名称);若是使用类调用,则第一个参数需要手动传值。
People.discribe(p)  # 通过类进行调用,就需要把对象传进去
hades is 27 years old

类的绑定方法

  • 类中使用 @classmethod 修饰的方法就是绑定到类的方法。这类方法专门为类定制。通过类名调用绑定到类的方法时,会将类本身当做参数传给类方法的第一个参数。
class A:
    count = 1
    
    @classmethod
    def f1(cls,obj):
        print(cls.count)
        cls.f2(obj)
        
    def f2(self):
        print(self,'from f2')
        
a = A()

A.f1(a)
1
<__main__.A object at 0x0000024E75EDDBA8> from f2
a.f1(a)
1
<__main__.A object at 0x0000024E75EDDBA8> from f2

非绑定方法

  • 在类内部使用 @staticmethod 修饰的方法即为非绑定方法,这类方法和普通定义的函数没有区别,不与类或对象绑定,谁都可以调用,且没有自动传值的效果。
class A:
    count = 1
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @classmethod
    def f1(cls,obj):
        print(cls.count)
        cls.f2(obj)
        
    @staticmethod
    def f2(self):
        print(self,'from f2')
        
        
a = A('hades',27)

A.f2(a)
a.f2()  # 报错,因为没有进行传参,也就证明了这个方法不是对象的绑定方法
<__main__.A object at 0x0000024E760BD198> from f2



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-96-632f4a11ba55> in <module>
     19 
     20 A.f2(a)
---> 21 a.f2()  # 报错,因为没有进行传参,也就证明了这个方法不是对象的绑定方法


TypeError: f2() missing 1 required positional argument: 'self'
原文地址:https://www.cnblogs.com/Hades123/p/11066773.html