封装的绑定与多态

一:特性(property)

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

利用这种特性可以把类的功能属性伪装成数据属性,更加方便的调用;

为什么要用property?

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

例如

BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

egon=People('egon',75,1.80)

egon.bmi=egon.weight / (egon.height * egon.height)
print(egon.bmi)
# 首先需要明确。bmi是算出来的,不是一个固定死的值,也就说我们必须编写一个功能,每次调用该功能都会立即计算一个值
egon=People('egon',75,1.80)
yl=People('yangli',85,1.74)
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    def bmi(self):
        return self.weight / (self.height * self.height)
# 但很明显人的bmi值听起来更像一个名词而非动词
# print(egon.bmi())
# print(yl.bmi())
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    @property
    def bmi(self):
        return self.weight / (self.height * self.height)
# egon.weight=70
# print(egon.bmi) 
# print(yl.bmi)
View Code

调用egon.bmi本质就是触发函数bmi的执行,从而拿到其返回值。但是,虽然看上去bmi是类的实例化对象的一个属性,但这只是伪装的表现,实际上它还是一个功能,因此要注意到:

egon.bmi=123,egon.bmi背后对应的是一个函数,所以不能赋值

del egon.bmi,本质是一个函数,也不能删除。

二:多态

2.1:什么是多态?

多态指一种食物的多种形态

比如动物,有猫,狗,猪等多种形式

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class Cat(Animal): #动物的形态之一:猫
    def talk(self):
        print('say miaomiao')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

2.2: 多态性

多态性是指不考虑实例类型的情况下使用实例

在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

比如:猪,叫(),猫.叫(),猪叫是“哼哼哼”,猫叫是“喵喵喵”,虽然二者消息一样,但是执行的效果不同。

多态性分为静态多态性和动态多态性
静态多态性:如任何类型都可以用运算符+进行运算
动态多态性:如下

cat=Cat()
dog=Dog()
pig=Pig()
#cat、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
cat.talk()
dog.talk()
pig.talk()
#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()

2.3: 为什么要用多态性

其实大家从上面多态性的例子可以看出,我们并没有增加什么新的知识,也就是说python本身就是支持多态性的,这么做的好处是什么呢?
1.增加了程序的灵活性
  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)。
2.增加了程序额可扩展性
  通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用。

2.4 :鸭子类型

即如果看起来像,且叫声和走路的样子像,那么它就是一只鸭子;

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

#二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass

三:面向对象的绑定方法和非绑定方法

3.1::绑定方法

绑定给谁,谁来调用就自动将它本身当作第一个参数传入;
在类内部定义的函数,默认就是给对象来用,而且是绑定给对象用的,称为对象的绑定方法。
绑定对象的方法特殊之处:
  应该由对象来调用,对象来调用,会自动将对象当作第一个参数传入
绑定到类的方法特殊之处:
  应该由类来调用,类来调用,会自动将类当作第一个参数传入
  绑定给类的方法(classmethod)
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法  

import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST,settings.PORT)

print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()

conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
View Code

3.2:非绑定方法

用staticmethod装饰器装饰的方法;
就是一个普通函数特性:既不跟类绑定,也不跟对象绑定,这意味着谁都能用,谁来用都是一个普通函数,也就是说没有自动传值的特性了。

定义一个非绑定方法

import hashlib
import time
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
    @staticmethod
    def create_id(): #就是一个普通工具
        m=hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()

print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
conn=MySQL('127.0.0.1',3306)
print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
View Code

非绑定方法使用实例

import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @staticmethod
    def from_conf():
        return MySQL(settings.HOST,settings.PORT)

    # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
    # def from_conf(cls):
    #     return cls(settings.HOST,settings.PORT)

    def __str__(self):
        return '就不告诉你'

class Mariadb(MySQL):
    def __str__(self):
        return '<%s:%s>' %(self.host,self.port)

m=Mariadb.from_conf()
print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:

mariadb是mysql
View Code

回到目录开始

posted @ 2018-04-16 15:57  Leslie-x  阅读(202)  评论(0编辑  收藏  举报