Python3-初识面向对象

 知识点:

  • 面向过程VS面向对象
  • 初识类和对象
  • 对象之间的交互
  • 类与对象之间的命名空间
  • 面向对象的组合用法
  • 面向对象的三大特性(封装、继承、多态)
  • 继承
  • 抽象类和接口类
  • 多态
  • 封装  -- (property装饰器)
  • 绑定方法和非绑定方法 -- (classmethod 和 staticmethod装饰器)

面向过程 VS 面向对象 

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,著名的例子有linux内核,git,以及apache http server等。

 

面向对象的程序设计的核心是对象(上帝思维模式),利用“类”和“对象”来创建各种模型来实现对真实世界的描述。

优点:解决了程序的扩展性,提高开发效率。对某一个对象单独修改,会立刻反映到整体体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法像面向过程的程序设计流水线式可以很精确的预测问题的处理流程和结果,面向对象的程序一旦开始就由对象之间的交互解决问题即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

 

面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

了解一些名词:类、对象、实例、实例化

类:具有相同特征的一类事物(人、狗、老虎)

对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

实例化:类——>对象的过程(这在生活中表现的不明显,我们在后面再慢慢解释)

 

初识类和对象

python中一切皆为对象,类型的本质就是对象。

在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。

 

初识类

类有两种作用:属性引用和实例化

1. 属性引用(类名.属性)

# 类会自动加载运行,不用加()(可用print调试)
class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

 

实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

实例化的过程就是 类-->对象的过程

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;
        
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")

原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。

语法:对象名 = 类名(参数)

egg = Person('egon')  #类名()就等于在执行Person.__init__()
#执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。

2. 实例化调用类的属性、方法

print(egg.name)     #查看属性直接 对象名.属性名
print(egg.walk())   #调用方法,对象名.方法名()

关于self

self:在实例化时自动将对象/实例本身传给__init__的第一个参数, 你可以起别名,但一般都不要这么做, 改了别人不认识。。

类属性的补充

一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
View Code

 

对象的相关知识

对象是关于类而实际存在的一个例子,即实例

面向对象小结——定义及调用的固定模式:

class 类名:
    def __init__(self,参数1,参数2):
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2

    def 方法名(self):pass

    def 方法名2(self):pass

对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
                  #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                  #括号里传参数,参数不需要传self,其他与init中的形参一一对应
                  #结果返回一个对象
对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

 

对象之间的交互

创建一个人类

class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人

    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;

    def attack(self,dog):  
        # 人可以攻击狗,这里的狗也是一个对象。
        # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降
        dog.life_value -= self.aggressivity

创建一个狗类

class Dog:  # 定义一个狗类
    role = 'dog'  # 狗的角色属性都是狗

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name  # 每一只狗都有自己的昵称;
        self.breed = breed  # 每一只狗都有自己的品种;
        self.aggressivity = aggressivity  # 每一只狗都有自己的攻击力;
        self.life_value = life_value  # 每一只狗都有自己的生命值;

    def bite(self,people):
        # 狗可以咬人,这里的狗也是一个对象。
        # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
     dog.life_value -= self.aggressivit

开始交互

# 实例化一个人
egg = Person('egon',10,1000)

#创造了一只实实在在的狗ha2
ha2 = Dog('二愣子','哈士奇',10,1000)  

交互 egon打ha2一下
print(ha2.life_value)         #看看ha2的生命值
egg.attack(ha2)               #egg打了ha2一下
print(ha2.life_value)         #ha2掉了10点血

 

类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。

而类中有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法

其中类的数据属性是共享给所有对象的

>>>id(egg.role)
4341594072
>>>id(Person.role)
4341594072

而类的动态属性是绑定到所有对象的

>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8> 

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性。

在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常

 

面向对象的组合用法

代码重用的重要方式除了继承之外还有另外一种方法,即:组合。  (类与类之间的关系: ' ' 有 ' ')   人有武器~

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合。

class Weapon:
    def prick(self, obj):   # 这是该装备的主动技能, 扎死对方
        obj.life -= 500       # 假设攻击力为500

class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人

    def __init__(self, name, life):
        self.name = name  # 每一个角色都有自己的昵称;
        self.weapon = Weapon()  # 给角色绑定一个武器;
        self.life = life

egg = Person('egon', 600)
p1 = Person('bb', 600)
egg.weapon.prick(p1)
print(p1.life)

 

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

 

 

面向对象的三大特性

继承

什么是继承

继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题

继承是一种创建新类的方式,子啊python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类曾为派生类或子类。

python中类的继承分为:单继承和多继承

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

查看继承

>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

 

经典类和新式类

1. 只有在python2中才分新式类和经典类, python3中统一都是新式类。
2. 在python2中,没有显式的继承object类的类, 以及该类的子类, 都是经典类
3. 在python2中,显式地声明继承object地类,以及该类地子类,都是新式类
4. 在python3中, 无论是否继承object, 都默认继承object,所以默认均为新式类。

 提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

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

 

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

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

抽象分成两个层次: 

1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

2.将人,猪,狗这三个类比较像的部分抽取成父类。

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

 

 

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    pass

class Person(Animal):
    pass

egg = Person('egon',10,1000)
ha2 = Dog('二愣子',50,1000)
egg.eat()
ha2.eat()

 

派生

当然子类可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名时,那么调用新增的属性时,就以自己为准了。

派生 : 子类在父类方法和属性的基础上产生了新的方法和属性
class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def bite(self, people):
        '''
        派生:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def attack(self, dog):
        '''
        派生:人有攻击的技能
        :param dog: 
        '''
        dog.life_value -= self.aggressivity

egg = Person('egon',10,1000)
ha2 = Dog('二愣子',50,1000)
print(ha2.life_value)
print(egg.attack(ha2))
print(ha2.life_value)

注意:像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值.

在python3中,子类执行父类的方法也可以直接用super方法.

class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()
        #A.hahaha(self)
        print('B')

a = A()
b = B()
b.hahaha()
super(B,b).hahaha()

 

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def __init__(self,name,breed,aggressivity,life_value):
        super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法
        self.breed = breed  #派生出了新的属性

    def bite(self, people):
        '''
        派生出了新的技能:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

    def eat(self):
        # Animal.eat(self)
        #super().eat()
        print('from Dog')

 

抽象类与接口类

接口类。 归一(提供统一接口,更方便用户)

。。。

抽象类(规范化子类)

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化!

如果说类时从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,包括静态属性和动态属性。

在python中实现抽象类

#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

###
def func(obj):
  func.obj()

抽象类与接口类

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计 

在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。

 

钻石继承

继承顺序

1. Python的类可以继承多个类, Java和C#中只能继承一个类。

2. Python的类如果继承了多个类, 那么其寻找的方法只有两种,分别是:深度优先和广度优先

当类是经典类时, 多继承情况下, 会按照深度优先查找

当类时新式类时, 多继承情况下, 会按照广度优先查找

广度优先,查找到有同一父类的那一层停止查找,跳到下一层, 由最后一层到达object层。

 

继承原理

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了实现继承,python会在MRO列表上从做到右开始查找基类,直到找到一个匹配这个属性的类为止。

父类的MRO列表遵循如下三条规则:

1. 子类会优先于父类检查

2. 多个父类会根据他们在列表中的顺序被检查

3. 如果对下一个类存在两个合法的选择,选择第一个父类

继承小结

继承的作用

减少代码的重用
提高代码可读性
规范编程模式

几个名词

抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
继承:子类继承了父类的方法和属性
派生:子类在父类方法和属性的基础上产生了新的方法和属性

抽象类与接口类

1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口


2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现

钻石继承

新式类:广度优先
经典类:深度优先

 

多态

多态指的是一类事物有多种形态

动物有多种形态:人,狗,猪

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

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

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

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

多态性(一个接口多种实现)

一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)

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

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

比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

多态性

peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()

鸭子类型

#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
# 二者没有继承关系
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

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

多态性与抽象类

多态性:只要类中有相同的方法即可,不用有直接的继承关系

抽象类:从多个类中抽取相像的属性组合成的类, 参与'同一接口,多种实现'的类必须继承这个抽象类,  达到了统一规范方法的效果。

 

封装

【封装】

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

【好处】 

1. 将变化隔离

  可以直接修改类内部的给外部使用内部属性的方法, 不影响用户调用此接口。(因为接口名字没变)

2. 便于使用

3. 提高复用性

4. 提高安全性

【封装原则】

  1. 将不需要对外部提供的属性都隐藏起来;

  2. 把属性都隐藏, 提供公共方法对其访问。

私有变量和私有方法

在python中是双下划线开头的方式将属性隐藏起来(设置成私有的)

私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

这种自动变形的特点:

1. 类中定义的__x只能在内部使用,如self.__x, 引用的就是变形的结果。

2. 这种变形其实正是针对外部的变形, 在内部是无法通过__x 这个名字访问到的。

3. 在子类定义的__x 不会覆盖在父类定义的__x, 因为子类中变形成了:_子类名, 而父类中变形成了:_父类名__x, 即在双下划线开头的属性在继承给基类时,子类是无法无盖的。

 

这种变形需要注意的问题是:

1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形

私有方法

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

>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定义成私有的,即__fa
>>> class A:
...     def __fa(self): #在定义时就变形为_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只会与自己所在的类为准,即调用_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(装饰器)是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值     ---  把动态属性变成静态属性给用户调用。

计算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**2)

# 不加装饰器想要得到BMI值
# p1 = People('egon', 75, 185)
# print(p1.bmi())

# 加了装饰器property
p1=People('egon',75,1.85)
print(p1.bmi)

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

用户虽然表面上调用的是类的静态属性,但在类里面这个属性还是属于动态属性的。用户想实现直接 obj.name 对该属性进行修改,是不行的。我们可以加装饰器来实现以修改、删除

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

    @property
    def name(self):
        # print('getter')
        return self.__name

    @name.setter
    def name(self,val):
        # print('setter',val)
        if not isinstance(val,str):
            print('名字必须是字符串类型')
            return
        self.__name=val

    @name.deleter
    def name(self):
        print('deleter')

        print('不允许删除')


p=People('egon')


# print(p.name)

# 修改
# p.name
# p.name='EGON'
# p.name=123
# print(p.name)

# 删除
del p.name

一个静态属性property本质就是实现了get,set,delete三种方法

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

 

绑定方法和非绑定方法

在类内部定义的函数,分为两大类:

  一:绑定方法:绑定给谁, 就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入

    绑定到对象的方法:在类内定义的没有被任何装饰器装饰的

    绑定到类的方法:在类内定义的被装饰器classmethod装饰的方法

 

  二:非绑定方法:没有自动传值的方法了,就类中定义的一个普通工具,对象和类都可以使用

    非绑定方法:不与类或者对象绑定

 

classmethod 和 staticmethod

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

    def tell(self):    # 默认无任何装饰器的方法 为绑定对象的方法
        print('名字是%s' %self.name)

    @classmethod    # 定义该方法为绑定到类的方法的装饰器
    def func(cls): #cls=Foo
        print(cls)

    @staticmethod    # 定义该方法为不绑定方法的装饰器
    def func1(x,y):
        print(x+y)

f=Foo('egon')    

print(f.tell)
print(f.func)
print(f.func1)

输出:
<bound method Foo.tell of <__main__.Foo object at 0x0000026C3A0515C0>>
<bound method Foo.func of <class '__main__.Foo'>>
<function Foo.func1 at 0x0000026C39F36A60>

 

绑定方法与非绑定方法的应用

import settings
import hashlib
import time

class People:
    def __init__(self,name,age,sex):
        self.id=self.create_id()
        self.name=name
        self.age=age
        self.sex=sex

    def tell_info(self): #绑定到对象的方法
        print('Name:%s Age:%s Sex:%s' %(self.name,self.age,self.sex))

    @classmethod
    def from_conf(cls):
        obj=cls(
            settings.name,
            settings.age,
            settings.sex
        )
        return obj

    @staticmethod
    def create_id():
        m=hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()

# p=People('egon',18,'male')

#绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入
# p.tell_info() #tell_info(p)

#绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入
# p=People.from_conf() #from_conf(People)
# p.tell_info()


#非绑定方法,不与类或者对象绑定,谁都可以调用,没有自动传值一说
p1=People('egon1',18,'male')
p2=People('egon2',28,'male')
p3=People('egon3',38,'male')

print(p1.id)
print(p2.id)
print(p3.id)

 

 

 

 

posted @ 2019-01-12 21:55  下山打老虎i  阅读(247)  评论(0编辑  收藏  举报