多态与封装

多态

什么是多态?

一种事物/一个类的多种形态。

例如:动物有多种形态:人、猪、狗;文件有多种形态:执行文件、文本文件;水有多种形态:液态水、固态水、水蒸气

import adc
class File(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def click(self):
        pass
    
class Text(File):
    def click(self):
        print('open file')
        
    
class ExeFile(File):
    def click(self):
        print('execute file')

多态性:多个不同类对象响应同一个方法,返回不同的结果

多态性的好处?

1、增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种方式去调用。如:fun(animal)

2、增加了程序的可扩展性:通过继承Animal类创建一个新类,使用者无需更改代码,还是用fun(animal)调用

'''多态性:一种调用方式,不同的执行效果(多态性)
    def func(obj):
        obj.run()
    func(peo1)
    func(pig1)
    func(d1)
    
    # 多态性依赖于:继承
	# 多态性:定义统一的接口
'''

class Cat(Animal):  # 属于动物的另外一种形态:猫
    def talk(self):
        print('say miao')

def func(animal):  # 对于使用者来说,自己的代码根本无需改动
    animal.talk()

cat1 = Cat()  # 实例出一只猫
func(cat1)  # 甚至连调用方式也无需改变,就能调用猫的talk功能

多态小结

多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
多态性:一种调用方式,不同的执行效果(多态性)

封装

什么是封装?

隐藏对象的属性和实现细节,仅对外提供公共访问方式

先装后封:

​ 装:把数据和方法装到类里面。

​ 封:把类里面的数据和方法封起来,不让人访问。

封装原则:将不需要对外提供的内容都隐藏起来。把属性都隐藏,提供公共方法对其访问。

为什么要封装:

1、保护隐私,比如:你买了个充气娃娃,你能让人知道吗?这时候就把你买的娃娃封装起来,然后自己偷偷用。

2、隔离复杂度,比如:你有尿尿这个功能(方法),你不需要知道你尿是经历了多少化学反应才流出来的,你只需要掏出你的接口直接用尿这个功能就行,这不就隔离了复杂度嘛。

封装的两个层面:

无论那种层面的封装,都要对外界提供接口,访问你内部隐藏的内容。使用者都只能通过这个接口访问。

1、创建类和对象会分别创建二者的名称空间,我们只能使用类名.或者对象.的方式访问名称空间里面的名字,这本身就是一种封装。

2、把某些属性隐藏起来(或者说定义成私有的),只在类内部使用,外部无法访问。或者留下少量接口,供外部访问。

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

重要:类中所有双下划线开头的名称如 __X 会自动变形 _类名__X 的形式

私有属性:用双下划线开头的属性

class A:
    __N = 0   #变形为_A__N
    def __init__(self):
        self.__X = 10   #变形成self._A__X
    
    def __foo(self):   #变形成:_A__foo
        pass
    
    def bar(self):
        self.__foo()   #只有在类内部可以通过__foo访问。
        
#注意:A.__N访问不到,A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形  

print(A.__dict__) # {'__module__': '__main__', '_A__N': 0, '__init__': <function A.__init__ at 0x1005704d0>, '_A__foo': <function A.__foo at 0x100570830>, 'bar': <function A.bar at 0x1004c2b00>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

print(A._A__N)  # 0 
a = A()
print(a._A__N)  # 0 
print(a.__N) # AttributeError: 'A' object has no attribute '__N'

自动变形的特点:

​	1、类中定义的 __x 只能在内部使用,如 self.__x, 引用的就是变形的结果
​	2、这种变形是针对外部的变形,在外部无法通过 self.__x 这个名字访问到。
​   3、在类中定义的 __x 不会覆盖父类定义的 __x, 因为子类中的 __x 变形成了 _子类__x, 父类中的 __x 则变形成了 _父类__x 
所以子类定义的私有属性是无法覆盖父类的。因为变形成了不同的属性

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

class A:
	def __fa(self): #在定义时,变形为_A__fa
		pritn('from A')
	def test(self):
		self.__fa()   #类内部调用自己的私有方法,实际上为self._A__fa()
        
class B(A):
    def __fa(self):
        print('from B')
        
b = B()
b.test()  # from A

封装在于明确区分内外,让类使用者可以修改封装内的东西而不影响外部调用。

#类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
        return self.__width * self.__length


#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area


#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high

#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()

property属性

什么是property属性?

property属性是一种特殊的属性。使用@property装饰器可以将一个函数当做数据属性来使用。

class A:
    def __init__(self, w, h):
        self.__w = w
        self.__h = h

    @property
    def bmi(self):
        return self.__w / self.__h ** 2

a = A(67, 1.76)
print(a.bmi) # 21.62964876033058
import math
class Circle():
    def __init__(self, radius):
        self.__radius = radius

    @property
    def area(self):
        return math.pi * self.__radius ** 2

    @property
    def perimeter(self):
        return 2 * math.pi * self.__radius


c = Circle(10)
print(c.area)
print(c.perimeter)

#注意:此时的特性area和perimeter不能被赋值
c.area=3 #为特性area赋值
'''
抛出异常:
AttributeError: can't set attribute
'''

为什么要用property?

将一个类的函数定义成特性以后,对象使用的时候obj.name,根本无法察觉自己的name是执行了一段函数然后就算出来的。这种特性的使用方式遵循了统一访问的原则。

property属性是无法修改的。python中提供了 @定义成特性的函数.setter 装饰器来修改,提供了 @定义成特性的函数.deleter 装饰器来防止删除。

class Foo:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('输入的名字必须是字符串类型的')
        self.__name = value

    @name.deleter
    def name(self):
        raise TypeError('特性name不能被删除')


f = Foo('大帅比')
print(f.name)

f.name = '我是你爹'
print(f.name)

f.name = 10  #TypeError: 输入的名字必须是字符串类型的

del f.name  # TypeError: 特性name不能被删除

'''
	注意:只有在函数被 @property 装饰器修饰成一个特性以后,才能使用 函数名。setter, 函数名.dellter
'''

@classmethod

类的绑定方法,当类里面的函数不需要用到对象的属性时可以使用类绑定方法

class Classmethod_Demo():
    role = 'dog'

    @classmethod
    def func(cls):
        print(cls.role)

Classmethod_Demo.func()

@staticmethod

静态方法,当类里面的函数不需要用到类属性、对象属性时,可以使用静态方法

class Staticmethod_Demo():
    role = 'dog'

    @staticmethod
    def func():
        print("当普通方法用")

Staticmethod_Demo.func()
posted @ 2019-07-29 17:13  KbMan  阅读(264)  评论(0编辑  收藏  举报