09 面向对象

面向对象 OOP

  • 面向对象是一种认识与分析世界的方法论
  • 将万事万物抽象为类
  • 类 class
    • 一类事物公共特征的集合
    • 用计算机语言描述就是属性与方法的集合
  • 对象 object / 实例 instance : 类的具体个体
  • 属性 : 对象状态的抽象
  • 操作/方法 : 对象行为的抽象,抽象为函数
  • 面向对象三要素 : 封装 继承 多态
    1. 封装
      • 将数据和操作组合在一起
      • 隐藏数据,对外只提供接口 类似黑箱
    2. 继承
      • 多复用,继承父类的属性与方法
      • 多继承少修改,使用继承来改变,体现个性
    3. 多态
      • 面向对象最灵活的地方,动态绑定

python类

  1. 使用class关键字
  2. 类名使用大驼峰 形如ClassName
  3. 类定义完后,产生一个类对象,绑定在标识符ClassName上
class Human:
    NAME='human name'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print(self.name,' is eating')

t=Human('tom', 20)
t.eat()
print(t.age,t.NAME)

实例化

  • t=Human(‘tom’, 20) 就是实例化
  • 每次实例化得到不同的实例
  • 即使实例化时使用同样的参数,也得到不同的实例
  • Python实例化,会调用 __init__(self)方法
  • 未定义__init__(self)则会在实例化时隐式调用
  • 作用 : 对实例进行初始化
  • __init__(self)的第一个参数self表示实例本身 (名称self是一种惯例)
  • 还有其他参数需要在实例化时填入

实例对象

  • 实例化后生成的对象称为实例对象
  • 实例对象绑定方法
    • 以上面的实例对象t为例
    • 调用方法时采用t.eat()的方式
    • 函数签名为eat(self)
    • self.name是实例的属性 保存在具体实例中,而不是保存在Human类.
  • 形如 self.name 的称为实例变量,为实例独有
  • 定义在类上的变量为类变量,所有实例共享
  • 一般来说,类变量使用全大写命名

特殊属性

  • 对象的特殊属性形如__attribute__
  • 是python里的magic
特殊属性 含义 实例对象 类对象
__name__ 对象名 报错 类名
__class__ 对象的类型 main.类名 type
__dict__ 对象的属性字典 实例属性字典 类属性字典
__qualname__ 类的限定名 报错 类名

- 类与实例的属性分别保存于对应的__dict__属性中
- 若要从实例访问类属性,可借助__class__找到所属的类

实例属性(实例变量)与类属性(类变量)优先级

  • 类变量是属于类的变量,所有实例亦可共享
  • 实例变量只有自身实例可以访问 其他实例与类都不可访问
  • 实例属性的查找顺序
    • 实例通过.访问属性
    • 先去自身实例查找对应属性(实例的__dict__),找到就返回
    • 找不到再去类里(类的__dict__)找同名属性,找到则返回
    • 还找不到则报错,没有对应属性
    • 若实例使用__dict__[变量名]访问变量,则不会查找所在类的字典

装饰类 : 增加类属性

  • 装饰器也可以装饰类
  • 要求 : 在Human类中增加NAME属性
  • 函数版本 :
class Human:
    AGE=8
print(Human.__dict__)

def addname(name,cls):
    c.NAME=name
    return cls
addname('tom',Human)
print(Human.__dict__)
  • 装饰器版本 :
def addname(name):
    def wrapper(cls):
        cls.NAME=name
        return cls
    return wrapper

@addname('tom') # Human=addname('tom')(Human)
class Human:
    AGE=8
print(Human.__dict__)

类方法与静态方法

  • 类方法
    1. 在类定义中,使用@classmethod装饰器修饰的方法
    2. 必须至少有一个参数,第一参数常命名为cls cls指代调用者,即类对象本身
    3. 首个参数命名为cls是一种约定 为了代码易读性请勿修改
    4. 通过cls可直接操作类的属性
    5. 类方法类似于C++ Java中的静态方法
class Human:  
    @classmethod
    def class_method(cls):  
        print('class={0.__name__}({0})'.format(cls))  
        cls.HEIGHT=170
Human.class_method()
Human.__dict__
  • 静态方法
    1. 在类定义中,使用@staticmethod装饰器修饰的方法
    2. 调用时,不会隐式地传入参数
class Human:
    @classmethod
    def class_method(cls):
        print('class={0.__name__}({0})'.format(cls))
        cls.HEIGHT=180

    @staticmethod
    def static_method():
        print(Human.HEIGHT)
  • 类调用普通方法会报错,
    • 因为普通方法第一参数需要传入实例本身
    • 类是抽象概念,无实例

私有(Private)属性

  • 属性名使用双下划线开头
  • 本质 : Python解释器将__变量名 改名为 _类名__变量名
  • 保护变量 : 变量名前加一个下划线
    • 保护变量与普通变量一样,解释器未做改名处理
    • 是开发者的共同约定,看做私有变量
  • 私有方法 同上
  • 函数名之前加下划线或双下划线

  • 私有方法的本质

  • 单下划线的方法只是开发者之间的约定,解释器不做任何改变
  • 双下划线的方法为私有方法.解释器会将__方法名 改名为 _类名__方法名
  • 私有方法在类的__dict__属性中可以找到
  • Python中没有绝对的私有属性
  • 只是双下划线开头的属性被改了名
  • 前导下划线只是一种警告与提醒,请遵守这一约定
  • 除非真有必要,否则请勿使用私有成员,更不要修改他们

property装饰器

  • 为防止属性被外部直接访问甚至修
  • 可定义函数用于获得以及修改属性
  • property装饰器可将函数装饰成属性
  • 同时定义该’属性’是否可变
  • 有装饰器与函数两种用法
  • 装饰器用法 :
import random
class RandInt:
    def __init__(self,count=3):
        self.__len=count
    def makenum(self,lrange=1,rrange=50):
        return [(random.randint(lrange,rrange)) for _ in range(self.__len)]
    @property
    def leng(self):
        return self.__len
    @leng.setter
    def leng(self,count):
        self.__len=count
  • 函数调用法 :
import random
class RandInt:
    def __init__(self,count=3):
        self.__len=count
    def makenum(self,lrange=1,rrange=50):
        return [(random.randint(lrange,rrange)) for _ in range(self.__len)]
    fget=lambda x:x.__len
    def fset(self,count):
        self.__len=count
    leng=property(fget, fset)
  • 以上两种代码都可将leng函数装饰为属性,实例可直接访问leng属性
  • 同时再加入@leng.setter装饰器(或在property函数的第二个参数传入fset函数)可直接通过赋值语句修改属性
  • fset函数可实现更严格的判断,条件具备才能更改属性值 可避免直接修改属性值

猜你喜欢

转载自blog.csdn.net/lk1115640590/article/details/80216463