类的装饰器,绑定方法与非绑定方法,继承,继承背景下的属性查找

一、类的装饰器

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
...     @property
...     def bmi(self):
...         return self.weight / (self.height**2)
...
>>> obj=People('lili',75,1.85)
>>> obj.bmi #触发方法bmi的执行,将obj自动传给self,执行后返回值作为本次引用的结果
21.913805697589478

使用property有效地保证了属性访问的一致性。另外property还提供设置和删除属性的功能,如下

>>> class Foo:
...     def __init__(self,val):
...         self.__NAME=val #将属性隐藏起来
...     @property
...     def name(self):
...         return self.__NAME
...     @name.setter
...     def name(self,value):
...         if not isinstance(value,str):  #在设定值之前进行类型检查
...             raise TypeError('%s must be str' %value)
...         self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
...     @name.deleter
...     def name(self):
...         raise PermissionError('Can not delete')
...
>>> f=Foo('lili')
>>> f.name
lili
>>> f.name='LiLi' #触发name.setter装饰器对应的函数name(f,’Egon')
>>> f.name=123 #触发name.setter对应的的函数name(f,123),抛出异常TypeError
>>> del f.name #触发name.deleter对应的函数name(f),抛出异常PermissionError

二、绑定方法与非绑定方法

类中定义的函数分为两大类:绑定方法和非绑定方法

其中绑定方法又分为绑定到对象的对象方法和绑定到类的类方法。

在类中正常定义的函数默认是绑定到对象的,而为某个函数加上装饰器@classmethod后,该函数就绑定到了类。

绑定方法的特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数传入

为类中某个函数加上装饰器@staticmethod后,该函数就变成了非绑定方法,也称为静态方法。该方法不与类或对象绑定,类与对象都可以来调用它,但它就是一个普通函数而已,因而没有自动传值那么一说

非绑定方法的特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通函数,没有自动传参的效果

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

    # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用,
    # 会将对象当作第一个参数自动传入
    def tell(self):
        print(self.name)

    # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用,
    # 类来调用会类本身当作第一个参数自动传入
    @classmethod
    def f1(cls):  # cls = People
        print(cls)

    # 类中定义的函数被staticmethod装饰过,就成一个非绑定的方法即一个普通函数,谁都可以调用,
    # 但无论谁来调用就是一个普通函数,没有自动传参的效果
    @staticmethod
    def f2(x,y):
        print(x,y)

p1 = People('egon')
# p1.tell()

# print(People.f1)
# People.f1()

# print(People.f2)
# print(p1.f2)

# People.f2(1,2)
# p1.f2(3,4)

三、继承

继承是创建新类的一种方式

新建的类称之为子类, 被继承的类称之为父类、基类、超类

继承的特点是:子类可以遗传父类的属性

类是用解决对象之间冗余问题的, 而继承则是来解决类与类之间冗余问题的

在python中支持多继承

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承
    pass

class SubClass2(ParentClass1,ParentClass2): #多继承
    pass

通过类的内置属性__bases__可以查看类继承的所有父类

>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

在Python2中有经典类与新式类之分,没有显式地继承object类的类,以及该类的子类,都是经典类,显式地继承object的类,以及该类的子类,都是新式类。而在Python3中,即使没有显式地继承object,也会默认继承该类,在python3中全都是新式类。如下

>>> ParentClass1.__bases__
(<class ‘object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)

继承与抽象(先抽象再继承)

继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承

抽象即抽取类似或者说比较像的部分。

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

四、继承背景下的属性查找

有了继承关系,对象在查找属性时,先从对象自己的--dict--中找,如果没有则去子类中找,然后再去父类中找……

>>> class Foo:
...     def f1(self):
...         print('Foo.f1')
...     def f2(self):
...         print('Foo.f2')
...         self.f1()
... 
>>> class Bar(Foo):
...     def f1(self):
...         print('Foo.f1')
... 
>>> b=Bar()
>>> b.f2()
Foo.f2
Foo.f1

b.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Foo.f1

父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的

>>> class Foo:
...     def __f1(self): # 变形为_Foo__fa
...         print('Foo.f1') 
...     def f2(self):
...         print('Foo.f2')
...         self.__f1() # 变形为self._Foo__fa,因而只会调用自己所在的类中的方法
... 
>>> class Bar(Foo):
...     def __f1(self): # 变形为_Bar__f1
...         print('Foo.f1')
... 
>>> 
>>> b=Bar()
>>> b.f2() #在父类中找到f2方法,进而调用b._Foo__f1()方法,同样是在父类中找到该方法
Foo.f2
Foo.f1
posted @ 2021-01-11 18:44  小绵  阅读(94)  评论(0编辑  收藏  举报