封装,继承,多态和绑定方法
封装,继承,多态
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)

浙公网安备 33010602011771号