面向对象

6. 面向对象

[TOC]

面向对象和面向过程编程

面向对象编程是一个编程思想,python中一切皆对象

'''
1. 面向对象编程  
  - 核心是’对象‘二字,对象指的是特征和技能的结合体
  - 基于该编程思想编写程序,就好比在创造世界,一种上帝式的思维方式
  - 优点:可扩展性高
  - 缺点:编写程序的复杂程度要远高于面向过程编程思想

2. 面向过程编程
  - 核心是'过程‘二字,过程指的是做事情的步骤,比如先做什么,在做什么
  - 基于该编程思想编写程序,就好比一条工厂的流水线,一种机械式的思维方式
  - 优点:逻辑清晰,复杂的问题流程化,进而简单化
  - 缺点:可扩展性差
'''

类和对象

调用类时自动触发,为不同的对象初始化定制属性
- '''
1. 定义
  - 类是一系列具有相同的属性和方法的对象的集合体,一般使用“驼峰体”命名
  - 对象是属性和方法的结合体,调用类即会产生对象,这个过程也叫做类的实例化,产生的对象称之为类的一个实例

2. 名称空间
  - 类的名称空间在定义阶段产生,执行python文件时会执行类内部的代码
  - 对象的名称空间在调用类的时候产生

3. 属性访问
  - 对象存放各自独有的属性,类中存放对象们共有的内容
  - 对象和类均可以通过.获取属性或方法
  - 对象可以获取自己和类的属性,类只能获取类属性

4.对象绑定方法
  - 由类调用类内部的函数,该函数只是一个普通的函数,普通函数需要接受几个参数,就得传入几个参数
  - 由对象来调用称之为对象的绑定方法,不同的对象调用该绑定方法,则会将不同的对象传入该绑定方法中,即self参数

5. __init__方法
  - 调用类时自动触发,为不同的对象初始化定制对象属性
  - 没有该方法,实例化时可以不传参数

6. __dict__方法,
  - 查看名称空间内的所有名字,即内部的方法和属性
  - 对象和类均有该方法
'''
class Student:
    school = '清华大学'

    # 该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def choose(self):
        print('%s is choosing a course' % self.name)


stu1 = Student('李建刚', '男', 28)  # 类的实例化
stu2 = Student('王大力', '女', 18)
stu3 = Student('牛嗷嗷', '男', 38)

print(stu1.name)  # 李建刚
stu1.choose()  # 李建刚 is choosing a course
print(Student.__dict__)
print(stu1.__dict__)  # {'name': '李建刚', 'sex': '男', 'age': 28}

面向对象的三大特性

封装

'''
1. 什么是封装
  - 封装指的是数据和功能整合到一起,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别
  - 把一堆属性封装到一个对象中,对象可以通过"."的方式获取属性,从而方便存取

2. 访问限制机制(隐藏属性)
  - 在类内部凡是以__开头的属性都会被隐藏,外部不能直接访问该属性,只有类内部可以直接访问
  - 该属性其实只是做了一个变形,检测语法时自动变成"_类名__属性",在类外部可以通过该方法访问
  - 一般会通过提供相应的接口让外部访问该属性(通过函数返回)

3. property
  - python内置装饰器,可以将类内部的函数变成数据属性,从而在调用的时候不用加括号
  - 被装饰过的属性不能被修改,如果非要修改可以用装饰器中的setter和deleter进行删改
'''
class Foo:
    __N = 0  # 变形为_Foo__N

    def __init__(self):
        self.__x = 10  # 变形为self._Foo__x

    # 开放接口,外界可以通过该方法获取_N值
    @property
    def f1(self):
        return self.__N

obj = Foo()
print(obj.f1)  # 0,不用加括号调用,获取内部隐藏属性

print(obj._Foo__x)  # 10  # 外部可通过_类名__属性访问
print(obj.__x)  # 报错

print(Foo._Foo__N)  # 0
print(Foo.__N)  # 报错

继承

'''
1. 什么是继承
  - 继承是一种创建新类的方式,是一系列类相同的特征与技能的结合体
  - 新建的类称之为子类/派生类,子类继承的类叫做父类/基类/超类
  - 子类可以继承父类的属性,并且可以派生出自己的属性,从而减少代码的冗余
  注意:在python中,一个子类可以有多个父类,其他语言子类只有一个父类

2. 怎么样继承
  - 先抽象(总结相似之处),再继承

3. 属性查找  (mro()方法可以查询当前类的继承顺序)
  - 先从对象自己的__dict__中找,如果没有则去类中找,然后再去父类中找

4. 派生与方法重用
  子类派生自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,因此需要重用父类方法
    - 父类函数调用
      直接通过父类.__init__,把__init__当作普通函数使用,传入对象和继承的属性
    - super()
      super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过'.'指向父类的名称空间

5. 新式类和经典类
  - 新式类:继承object的类以及其子类就叫新式类
    - python3中所有的类默认继承object类,即所有类都是新式类
    - python2中需要手动继承
  - 经典类:没有继承object的类以及其子类,就叫经典类
    - python3中不存在经典类
    - 只有python2中才有经典类

6. 钻石继承
  在多继承的情况下形成的钻石继承 (继承顺序),经典类与新式类会有不同MRO(方法解析顺序)
        - 经典类:深度优先(当查找属性不存在时的查找顺序)
        - 新式类:广度优先(当查找属性不存在时的查找顺序)

7. 组合  
  - 组合指的是在一个类中以另外一个类的对象作为数据属性,是类和对象的关系
'''

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

# 方法重用方式1:父类函数调用
class Teacher(People):
  def __init__(self,name,sex,age,title):
    People.__init__(self,name,age,sex) #调用的是函数,因而需要传入self
    self.title=title

# 方法重用方式2:super()
class Teacher(People):
  def __init__(self,name,sex,age,title):
    super().__init__(name,age,sex) #调用的是绑定方法,自动传入self
    self.title=title

多态

'''
1. 多态和多态性
  - 多态:指一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
  - 多态性:指具有不同功能的函数可以使用相同的函数名,同一种调用方式,会有不同的执行效果

2. 目的
  - 让不同类型的对象,调用同一个方法名去执行不同的功能
  - 父类:定义要统一的标准
  - 子类:遵循父类的标准
  - 最终目的:统一子类编写的规范,让使用者可以更方便的调用相同功能的方法

3. 实现方式
  - 继承
  - 抽象类

4. 抽象类(abc模块,强制子类必须遵循父类的标准)
  - 定义类时,设置参数metaclass=abc.ABCMeta
  - 定义函数时,加上装饰器 @abc.abstractmethod
  - 这样继承该类的子类中必须实现被装饰的方法名

5. 鸭子类型
  - “如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”
  - 优点:鸭子类型相较于继承,耦合度低,扩展型强
  注意:在python中,不推荐使用抽象类强制限制子类的的定义,但是推荐类都遵循鸭子类型
'''

# 抽象类
import abc
class Animal(metaclass=abc.ABCMeta):
    # 吃
    @abc.abstractmethod
    def eat(self):
        pass

    # 喝
    @abc.abstractmethod
    def drink(self):
        pass

    # 叫
    @abc.abstractmethod
    def speak(self):
        pass

# 猪
class Pig(Animal):
    # 吃
    def eat(self):
        print('猪在吃饭')
        pass

    # 喝
    def drink(self):
        pass

    def speak(self):
        print('哼哼哼~~~')

    # 派生
    def run(self):
        pass

pig = Pig()

绑定方法和非绑定方法

'''
1. 绑定方法
  - 对象绑定方法:类中正常定义的函数默认是绑定到对象的,对象调用时会将自身当做第一个参数传入
  - 类的绑定方法:加上装饰器@classmethod的函数,类调用时会将自身当做第一个参数传入

2. 非绑定方法:类中的加上装饰器@staticmethod后的函数,也成为静态方法
  - 该方法不与类或对象绑定,类与对象都可以来调用它,但它就是一个普通函数而已,不会自动传值
'''
class People:
    # 类的绑定方法
    @classmethod
    def tell_info(cls):
        print(f'我是类{cls}的绑定方法')

    @staticmethod
    def create_id():
        print('我是静态方法')       

People.tell_info()   # 我是类People的绑定方法
People.create_id()  # 我是静态方法

反射

'''
1. 什么是反射
  - 在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用

2. 反射的函数
    - hasattr:通过字符串判断对象或类是否存在该属性
    - getattr:通过字符串,获取对象或类的属性
    - setattr:通过字符串,设置对象或类的属性
    - delattr:通过字符串,删除对象或类的属性  
'''

class People:
    country = 'China'
    def __init__(self,name,age,sex):
        self.name = name 
        self.age = age
        self.sex = sex        
p = People('wick',24,'man')

# hasattr
print(hasattr(p,'name'))  # True
print('name' in p.__dict__)  # True 


# getattr
print(getattr(p,'name','tank'))  # wick
print(p.__dict__.get('name'))  # wick


# setattr
setattr(p,'sal','3.0')
print(hasattr(p,'sal'))  # True


# delattr
delattr(p,'sal')  #  相当于del p.level
print(hasattr(p,'sal'))  # False

类的内置方法(魔法方法)

'''
1. 类的内置方法
  - 凡是在类内部定义,以__开头和__结尾的方法都称之为魔法方法
  - 魔法方法会在某些条件成立时触发

2. 有哪些内置方法
    __init__:在调用类时触发
    __str__:会在打印对象时触发,必须要返回值,必须是字符串类型
    __del__:对象被销毁前执行该方法
    __delattr__:删除属性的时候触发该方法
    __getattr__:会在对象.属性时,属性没有才会触发
    __setattr__:会在对象.属性 = 属性时触发
    __getattribute__:对象.属性时,不管属性有没有都会触发,和__getattr__共存时,只会触发该方法
    __call__:会在对象被调用时触发
    __new__:会在__init__执行前触发

    __enter__:出现with语句触发,有返回值则赋值给as声明的变量
    __exit__:with中代码块执行完毕时执行

    __doc__:可以获取类的描述信息(注释)
    __module__:获取当前操作对象在哪个模块
    __class__:获取当前对象的类
'''
class Foo(object):

    def __new__(cls, *args, **kwargs):
        print(cls)
        return object.__new__(cls,*args, **kwargs)  # 真正产生一个空对象

    # 若当前类的__new__没有return一个空对象时,则不会触发。
    def __init__(self):
        print('在调用类时触发...')

    def __str__(self):
         print('会在打印对象时触发...')
         # 必须要有一个返回值, 该返回值必须是字符串类型
         return '[1, 2, 3]'

    def __del__(self):
        print('对象被销毁前执行该方法...')

    def __getattr__(self, item):
        print('会在对象.属性时,“属性没有”的情况下才会触发...')
        print(item)
        # 默认返回None, 若想打印属性的结果,必须return一个值
        return 111

    # 注意: 执行该方法时,外部“对象.属性=属性值”时无效。
    def __setattr__(self, key, value):
        print('会在 “对象.属性 = 属性值” 时触发...')
        print(key, value)
        print(type(self))
        print(self, 111)
        self.__dict__[key] = value

    def __call__(self, *args, **kwargs):
        print(self)
        print('调用对象时触发该方法...')

单例模式

'''
1. 单例模式指的是单个实例,实例指的是调用类产生的对象
  - 实例化多个对象会产生不同的内存地址,单例可以让所有调用者在调用类产生对象时都指向同一个内存地址,比如打开文件
  - 目的:减轻内存的压力

2. 实现单例的几个方法
  - __new__方法中做判断
  - classmethod:通过类的绑定方法进行实例化
  - 装饰器
  - import
  - 共享属性
'''

# 1. __new__
class A:
    def __new__(cls,*args,**kwargs):
        if not hasattr(cls,'_instance'):
            cls._instance =object.__new__(cls)
        return cls._instance

# 2. classmethod    
class A:
    @classmethod
    def singleton(cls,*args,**kwargs):
        if not hasattr(cls,'_instance'):
            cls._instance = cls(*args,**kwargs)
        return cls._instance

for i in range(3):
    a = A.singleton()
    print(id(a))

# 3. 装饰器
def singleton(cls):
    def inner(*args,**kwargs):
        if not hasattr(cls,"_instance"):
            cls._instance = cls(*args,**kwargs)
        return cls._instance
    return inner

@singleton
class A:
    pass

# 4. import
# index.py
class A:
    pass
a = A()

# test.py
from index import a
for i in range(3):
    print(id(a))

# 5. 共享属性
class A(object):
    c = 1
    def __new__(cls, *args, **kwargs):
        ob = super().__new__(cls, *args, **kwargs)
        ob.__dict__ = {}
        return ob

for i in range(3):
    a = A()
    print(id(a.c))

元类

'''
1. 什么是元类(类的类)
  - python中一切皆对象(包括类),负责产生这个类的类就叫做元类
  - class创建类时,内部会自动调用元类type创建类,因此继承了type的类就是一个元类
  - 作用:控制类的创建 

2. class关键字创建类的流程分析
  class创建类时,内部会自动调用元类type,type会帮我们创建一个自定义类,此时会传入三个参数:
        - 类名:class_name
        - 类的基类们:class_bases = (object,)
        - 类的名称空间:class_dic,由执行类体代码得到的   

3. 自定义元类控制类的创建
  - 自定义一个继承了type的元类
  - __init__方法中控制类的创建
  - __call__方法中模拟type元类调用__new__生成空对象
    - 调用__new__产生空对象
    - 调用__init__方法初始化对象obj
    - 返回初始化的对象obj
  - 定义类时通过metaclass参数指定好元类,从而控制类的创建
'''
class MyMeta(type):
    # 控制对象的创建
    # def __new__(cls,class_name,class_base,class_dic)
    # 控制类的定义
    def __init__(self,class_name,class_base,class_dic):
        # 判断首字母是否大写
        if not class_name.istitle():
            raise TypeError ('类的首字母必须大写')

        # 控制类中必须要有注释
        if not class_dic.get('__doc__'):
            raise TypeError('类内部必须要写注释')

        # 模拟type元类内部做的事情
        def __call__(self,*args,**kwargs):
            # 1. 调用__new__创建一个空对象
            obj = object.__new__(self)

            # 2. 执行__init__(self,*args,**kwargs)
            obj.__init__(*args,**kwargs)
            return obj

class Foo(object,metaclass=MyMeta):
    '''注释'''
    x= 10
    def __init__(self,a,b):
        self.a = a
        self.b = b

    def f1(self):
        print('f1')

foo = Foo(10,20)  # 调用Foo对象,会触发__call__
print(foo.b)

isinstance和issubclass

'''
1. isinstance(obj,cls):
  - 检查obj是否是类 cls 的对象

2. issubclass(sub, super)
  - 检查sub类是否是super类的子类
'''
class Foo:
    pass

class Goo:
    pass

foo_obj = Foo()
print(isinstance(foo_obj,Foo))  # True
print(isinstance(foo_obj,Goo))  # False

print(issubclass(Goo,Foo))  # False

posted on 2025-12-05 11:20  wickyo  阅读(0)  评论(0)    收藏  举报