封装,继承,多态和绑定方法

封装,继承,多态

1.封装

1.1 封装属性隐匿

封装是面向对象的三大特征之一,但是如何将里面的属性隐匿呢,在属性的名前加__ 前缀,就会得到一个对外的隐匿属性的效果
该隐藏需要注意的问题:
I:在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:__类名__属性,然后就可以访问了,如Foo._A__N,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。

class Foo:
    __x = 1  # _Foo__x

    def __f1(self):  # _Foo__f1
        print('from test')


print(Foo.__dict__)
print(Foo._Foo__x)
print(Foo._Foo__f1)

但是通过类名加属性名称,你是访问不到的,如果想要访问,必须用 _Foo__x,设计者在设计的时候,肯定是不会让你取访问到他的属性,但是可以给你提供一个接口去访问
II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形

class Foo:
    __x = 1  # _Foo__x = 1

    def __f1(self):  # _Foo__f1
        print('from test')

    def f2(self):
        print(self.__x) # print(self._Foo__x)
        print(self.__f1) # print(self._Foo__f1)

print(Foo.__x)
print(Foo.__f1)
obj=Foo()
obj.f2()

他在外部访问不到,但是可以通f2来访问,f2就是提供给你的接口
III: 这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形

    __x = 1  # _Foo__x = 1

    def __f1(self):  # _Foo__f1
        print('from test')

    def f2(self):
        print(self.__x) # print(self._Foo__x)
        print(self.__f1) # print(self._Foo__f1)

Foo.__y=3
print(Foo.__dict__)
print(Foo.__y)

class Foo:
    __x = 1  # _Foo__x = 1

    def __init__(self,name,age):
        self.__name=name
        self.__age=age

obj=Foo('egon',18)
print(obj.__dict__)
print(obj.name,obj.age)

1.2 属性为什么要隐藏

 I、隐藏数据属性"将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制:
# 设计者:cccc
class People:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        # 通过该接口就可以间接地访问到名字属性
        # print('小垃圾,不让看')
        print(self.__name)

    def set_name(self,val):
        if type(val) is not str:
            print('小垃圾,必须传字符串类型')
            return
        self.__name=val

# 使用者:xxx
obj = People('egon')
# print(obj.name) # 无法直接用名字属性
# obj.set_name('EGON')
obj.set_name(123123123)
obj.get_name()

II、隐藏函数/方法属性:目的的是为了隔离复杂度

2.装饰器property

是用来绑定给对象的方法伪造成一个数据属性
案例一:

class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    # 定义函数的原因1:
    # 1、从bmi的公式上看,bmi应该是触发功能计算得到的
    # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值
    #    说白了,每次都是需要临时计算得到的

    # 但是bmi听起来更像是一个数据属性,而非功能
    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


obj1 = People('zc', 70, 1.83)
print(obj1.bmi())

obj1.height=1.86
print(obj1.bmi())

print(obj1.bmi)

案例三:

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


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

    @name.setter
    def name(self, val): # obj1.name='EGON'
        if type(val) is not str:
            print('必须传入str类型')
            return
        self.__name = val

    @name.deleter
    def name(self): # del obj1.name
        print('不让删除')
        # del self.__name


obj1=People('egon')
# 人正常的思维逻辑
print(obj1.name) #
# obj1.name=18
# del obj1.name

3.继承

3.1 继承的语法

I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
II:需要注意的是:python支持多继承.在Python中,新建的类可以继承一个或多个父类

III:python的多继承
优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
缺点:
1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
2、代码可读性会变差
3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins

class Parent1(object):
    x=1111

class Parent2(object):
    pass

class Sub1(Parent1): # 单继承
    pass

class Sub2(Parent1,Parent2): # 多继承
    pass

print(Sub1.__bases__)
print(Sub2.__bases__)

print(Sub1.x)

注意点:
在python2中有经典类与新式类之分
新式类:继承了object类的子类,以及该子类的子类子子类。。。
经典:没有继承object类的子类,以及该子类的子类子子类。。。

ps2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
print(Parent1.__bases__)
print(Parent2.__bases__)

3.2 如何实现继承


class OldboyPeople:
    school = 'OLDBOY'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class Student(OldboyPeople):
    def choose_course(self):
        print('学生%s 正在选课' % self.name)
# stu_obj = Student('lili', 18, 'female')
# print(stu_obj.__dict__)
# print(stu_obj.school)
# stu_obj.choose_course()


class Teacher(OldboyPeople):
    #           老师的空对象,'egon',18,'male',3000,10
    def __init__(self, name, age, sex, salary, level):
        # 指名道姓地跟父类OldboyPeople去要__init__
        OldboyPeople.__init__(self,name,age, sex)
        self.salary = salary
        self.level = level

    def score(self):
        print('老师 %s 正在给学生打分' % self.name)

tea_obj=Teacher('egon',18,'male',3000,10)
# print(tea_obj.__dict__)
# print(tea_obj.school)

tea_obj.score()

3.3 属性的查找

class Foo:
    def __f1(self): # _Foo__f1
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1() # self._Foo__f1,# 调用当前类中的f1

class Bar(Foo):
    def __f1(self): # _Bar__f1
        print('Bar.f1')

obj=Bar()
obj.f2()

# Foo.f2
# Foo.f1

3.3 继承的原理

3.3.1

菱形问题介绍与MRO

class A(object):
    # def test(self):
    #     print('from A')
    pass

class B(A):
    def test(self):
        print('from B')
    pass

class C(A):
    # def test(self):
    #     print('from C')
    pass

class D(C,B):
    # def test(self):
    #     print('from D')
    pass
    
print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表

obj = D()
obj.test()

3.3.2 广度和深度

class E:
    def test(self):
        print('from E')


class F:
    def test(self):
        print('from F')


class B(E):
    def test(self):
        print('from B')


class C(F):
    def test(self):
        print('from C')


class D:
    def test(self):
        print('from D')


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass


print(A.mro())
'''
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
'''

obj = A()
obj.test() # 结果为:from B

如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

class G: # 在python2中,未继承object的类及其子类,都是经典类
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class F(G):
    def test(self):
        print('from F')

class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

# 新式类
# print(A.mro()) # A->B->E->C->F->D->G->object

# 经典类:A->B->E->G->C->F->D
obj = A()
obj.test() #

总结:

1、继承结构尽量不要过于复杂
2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系

3.4mixins机制

多继承的正确打开方式:mixins机制
mixins机制核心:就是在多继承背景下尽可能地提升多继承的可读性
ps:让多继承满足人的思维习惯=》什么"是"什么


class Vehicle:
    pass

class FlyableMixin:
    def fly(self):
        pass

class CivilAircraft(FlyableMixin,Vehicle):  # 民航飞机
    pass

class Helicopter(FlyableMixin,Vehicle):  # 直升飞机
    pass

class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass


import socketserver
# 补充:通常Mixin结果的类放在左边

3.5 在子类派生的新方法中如何重用父类的功能

方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系

class OldboyPeople:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

    def f1(self):
        print('%s say hello' %self.name)


class Teacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        OldboyPeople.__init__(self,name,age,sex)

        self.level = level
        self.salary=salary

tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)

方式二:super()调用父类提供给自己的方法=》严格依赖继承关系
调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性

class OldboyPeople:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

    def f1(self):
        print('%s say hello' %self.name)


class Teacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        # super(Teacher,self).__init__(name,age,sex)
        super().__init__(name,age,sex) # 调用的是方法,自动传入对象

        self.level = level
        self.salary=salary
print(Teacher.mro())
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)

super()案例


class A:
    def test(self):
        print('from A')
        super().test()

class B:
    def test(self):
        print('from B')

class C(A,B):
    pass


obj=C()
obj.test()

print(C.mro())

class A:
    def test(self):
        print('from A')
        super().test1()

class B:
    def test(self):
        print('from B')

class C(A,B):
    def test1(self):
        print('from C')

obj=C()
obj.test()

print(C.mro())

4 多态

4.1 多态:同一事物有多种形态

class Animal:
    pass

class People(Animal):
    pass

class Dog(Animal):
    pass

class Pig(Animal):
    pass


2、为何要有多态=》多态会带来什么样的特性,多态性
   多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
class Animal: # 统一所有子类的方法
    def say(self):
        print('动物基本的发声频率。。。',end=' ')

class People(Animal):
    def say(self):
        super().say()
        print('嘤嘤嘤嘤嘤嘤嘤')

class Dog(Animal):
    def say(self):
        super().say()
        print('汪汪汪')

class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')


obj1=People()
obj2=Dog()
obj3=Pig()


obj1.say()
obj2.say()
obj3.say()

定义统一的接口,接收传入的动物对象

def animal_say(animal):
    animal.say()

animal_say(obj1)
animal_say(obj2)
animal_say(obj3)


print('hello'.__len__())
print([1,2,3].__len__())
print({'a':1,'b':2}.__len__())

def my_len(val):
    return val.__len__()

print(my_len('hello'))
print(my_len([1,2,3]))
print(my_len({'a':1,'b':2}))

len('hello')
len([1,2,3])
len({'a':1,'b':2})

4.2python推崇的是鸭子类型

class Cpu:
    def read(self):
        print('cpu read')

    def write(self):
        print('cpu write')

class Mem:
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')


class Txt:
    def read(self):
        print('txt read')

    def write(self):
        print('txt write')


obj1=Cpu()
obj2=Mem()
obj3=Txt()

obj1.read()
obj1.write()

obj2.read()
obj2.write()

obj3.read()
obj3.write()

了解:

import abc

class Animal(metaclass=abc.ABCMeta): # 统一所有子类的标准
    @abc.abstractmethod
    def say(self):
        pass

# obj=Animal() # 不能实例化抽象类自己

class People(Animal):
    def say(self):
        pass

class Dog(Animal):
    def say(self):
        pass

class Pig(Animal):
    def say(self):
        pass

#
# obj1=People()
# obj2=Dog()
# obj3=Pig()

5 绑定方法

绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入
1、绑定给对象的方法:调用者是对象,自动传入的是对象
2、绑定给类的方法:调用者类,自动传入的是类


import settings
(IP='127.0.0.1' 
PORT=3306)

class Mysql:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

    def func(self):
        print('%s:%s' %(self.ip,self.port))

    @classmethod # 将下面的函数装饰成绑定给类的方法
    def from_conf(cls):
        print(cls)
        return cls(settings.IP, settings.PORT)

# obj1=Mysql('1.1.1.1',3306)

obj2=Mysql.from_conf()
print(obj2.__dict__)

非绑定方法-》静态方法:
没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果

class Mysql:
    def __init__(self,ip,port):
        self.nid=self.create_id()
        self.ip=ip
        self.port=port

    @staticmethod # 将下述函数装饰成一个静态方法
    def create_id():
        import uuid
        return uuid.uuid4()

    @classmethod
    def f1(cls):
        pass

    def f2(self):
        pass
obj1=Mysql('1.1.1.1',3306)

# print(Mysql.create_id)
# print(obj1.create_id)

# Mysql.create_id(1,2,3)
# obj1.create_id(4,5,6)

print(Mysql.create_id)
print(Mysql.f1)
print(obj1.f2)
posted @ 2020-04-08 14:51  小子,你摊上事了  阅读(130)  评论(0)    收藏  举报