面向对象补充

本文详细探讨了面向对象编程的三大特性——继承、封装和多态,并通过实例解释了它们的概念和应用场景。同时,文章也提到了类的成员修饰符、特殊方法如`__call__`和`__new__`,以及元类`metaclass`在Python中的作用。面向对象在Django和DRF等框架中的应用也被提及,强调了它们在构建复杂系统时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象三大特性

  1. 继承
  • super作用:执行mro的下一个类中的相关方法
class Base:
    def func(self):
        print('base.func')
        super().func()  # 执行mro的下一个类中的func


class Foo:
    def func(self):
        print('foo.func')


class Bar(Base, Foo):
    pass


o = Bar()

o.func()

运行结果

base.func
foo.func

图示
在这里插入图片描述

  • self到底是谁
    如果一个类a,继承了另外一个类b,这个类还继承了一个类c,如果a中有个self.方法d,有些在a中找,如果a没有就去b找,b没有去c找,再c中找到此方法d后,该方法中有self.方法f,又要回到最初的a中找。
  1. 封装
  • 将一些函数封装到一个类中,就是面向对象的思想。
    如一个py文件中有50个函数,由于很乱,所以分门别类整理好,找的时候比较方便。
    class DB():与DB相关的方法,class Cache:与缓存相关的方法
  • 将一些数据封装到一个对象中
    有些接口返回给用户json数据,一个字典,再接口中创建一个字典,再返回给用户数据获取与否的状态等。但如果要写20个接口,对应20个函数,意味着再每个函数中都定义这个字典,在重复过程中容易出错,所以把这个字典封装到一个对象中。实例化这个类,引用这个对象的数据,相对于前者比较简介且标准化
    两者方法对比:
    在这里插入图片描述
    应用场景:
    DRF:requset即封装了Django的request又进一步封装了DRF的相关内容。
  1. 多态
    python本身就是多态(鸭子模型),
    如图:arg既可以传o,可以传b,只要能send
    在这里插入图片描述
    对比java的多态:
def func(string: arg):  # arg指定了只能传string
    arg.send()
    # java的多态局限性:arg可以是string对象也可以是string的派生类的对象。
    # 而python中可以是任意对象
  • 面向对象的应用场景

    • drf的视图,写接口时继承的视图类:apiview,modelviewside等等
    • drf中request的封装
  • 面向对象中classmethod和staticmethod区别
    @classmethod
    @staticmethod
    共同点:都通过类调用,不用通过对象就可调用,staticmethod可以没有参数,classmethod至少有个个参数:类。

  • 面向对象的成员修饰符

    • 公有
    • 私有:双下滑线表示私有,只能自己类来调用,别的类调用不了。包括派生类(继承的类)
  • 面向对象的成员

class Foo:
    a = 123  # 类变量/静态字段、静态数据

    def __init__(self):
        self.name = 'Lau'  # 实例变量/字段/属性


    def func(self):  # 方法/绑定方法:绑定到类中的方法
        pass


    @classmethod
    def func(cls):   # 类方法
        pass


    @staticmethod
    def func():     # 静态方法
        pass


    @property
    def json(self):  # 属性
        return ...
  • 对象和对象之间能否相加

一般不能相加,如:

class F:
    pass


class B:
    pass


o1 = F()
o2 = B()

r = o1 + o2

运行结果

    r = o1 + o2
TypeError: unsupported operand type(s) for +: 'F' and 'B'

增加__add__方法

class F:
    def __init__(self, num):
        self.num = num
    def __add__(self, other):
        return self.num + other.num
    

class B:
    def __init__(self, num):
        self.num = num

o1 = F(2)
o2 = B(4)

r = o1 + o2  # 执行o1中的__add__方法,把后面对象当做参数传进去,
print(r)   # >>>6
  • 面向对象特殊方法
    • 双下滑线
    • _dict_
    • _call_:对象加括号执行其__call__方法,flask,django请求进来的入口都是在__call__这个方法中,一个web框架首先要做两步:准备工作:运行socket到recv等待请求到来,到来时立即执行操作,这步都是从__call__方法开始的
      图示
      flask:

在这里插入图片描述
一旦请求进来,run_simple中的第三个参数self就会加括号,也就是对象加括号执行这个对象的__call__方法
在这里插入图片描述
django:
在这里插入图片描述
有些框架如果依赖wsgi,请求入口都比较相似。

  • _new_
    在__init__初始化之前,__new__返回什么,这个对象就是什么,构造方法
class F(object):
    pass

o = F()

print(o)  # >>> <__main__.F object at 0x000001D4ED79E390>

class F:
    def __new__(cls, *args, **kwargs):
        return 1

o = F()

print(o) # >>>  1

在这里插入图片描述
所以实例化某些对象时,有时对象的结果是不一样的,如DRF中的序列化时传的参数不同,对象的值不同
如:
在这里插入图片描述

  • 单例模式
    设计模式的一种,实例化一个对象,无论什么时候实例化都是这个对象,
    基本流程:
class Singleton:
    instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = super().__new__(cls)
        return cls.instance

obj1 = Singleton()
print(obj1)
obj2 = Singleton()
print(obj2)

运行结果

<__main__.Singleton object at 0x000002B73FBBB9E8>
<__main__.Singleton object at 0x000002B73FBBB9E8>

两者内存地址一样
但是单例模式不加锁时错误的,在多进程或多线程中能体现出来,
如:

import threading
import time

class Singleton:
    instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.instance:
            time.sleep(5)
            cls.instance = super().__new__(cls)
        return cls.instance

def task(a):
    o = Singleton()
    print(o)

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()

运行结果:

<__main__.Singleton object at 0x000002F0170DB9E8>
<__main__.Singleton object at 0x000002F017299898>
<__main__.Singleton object at 0x000002F0172BF4A8>
<__main__.Singleton object at 0x000002F0172A23C8>
<__main__.Singleton object at 0x000002F0172AE780>
<__main__.Singleton object at 0x000002F0172CA0F0>
<__main__.Singleton object at 0x000002F01730A978>
<__main__.Singleton object at 0x000002F0172CA0F0>
<__main__.Singleton object at 0x000002F01730A128>
<__main__.Singleton object at 0x000002F0172CA0F0>

由于创建对象时一起创建的,所以每个结果不一样,可以加锁来控制

import threading
import time

class Singleton:
    instance = None
    lock = threading.RLock()

    def __new__(cls, *args, **kwargs):
        with cls.lock:
            if not cls.instance:
                time.sleep(5)
                cls.instance = super().__new__(cls)
            return cls.instance



def task(a):
    o = Singleton()
    print(o)



for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()

运行结果

<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>

但是加锁和解锁浪费资源,为了优化代码性能,在对象已经存在的情况下直接返回该对象
代码更改如下:

import threading
import time

class Singleton:
    instance = None
    lock = threading.RLock()

    def __new__(cls, *args, **kwargs):
        if cls.instance:
            return cls.instance
        with cls.lock:
            if not cls.instance:
                time.sleep(5)
                cls.instance = super().__new__(cls)
            return cls.instance

def task(a):
    o = Singleton()
    print(o)

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()
  • item
    _getitem_
    _setitem_
    _delitem_
    Django中的session存取值session[’’] = value,一个对象加中括号,并传值,
class Session:
    pass

obj = Session()
obj['x1'] = 123

运行结果

    obj['x1'] = 123
TypeError: 'Session' object does not support item assignment

要使得其支持以上操作,就要定义__setitem__方法, 类似+触发__add__方法一样,


class Session:
    def __setitem__(self, key, value):
        print(key, value)


    def __getitem__(self, item):
        print(item)


    def __delitem__(self, key):
        print(key)


obj = Session()
obj['x1'] = 123
obj['x1']
del obj['x1']

运行结果

x1 123
x1
x1

Django源码:
在这里插入图片描述

  • _iter_ 有此方法,并返回一个迭代器, 就是可迭代对象
  • 面向对象上下文管理
    -_enter_
    -_exit_
class Foo():

    def __enter__(self):
        print('进入')
        return 333

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('退出')

obj = Foo()

with obj as x:  # __enter__返回什么,x就是什么
    print(x)
    print('操作中...')  # 执行完其中的代码,自动执行__exit__退出

运行结果

进入
333
操作中...
退出

应用场景:
文件句柄,lock,数据库的连接和断开,flask的localstack对象的上下文管理

  • 队列
    类似管道,放一个取走一个,先进先出

  • 后进先出,先进后出
    弹夹最后一个先打出去
    代码
class Stack:
    
    def __init__(self):
        self.container = []


    def push(self, value):
        '''
        向栈中插入数据
        :param value:
        :return:
        '''
        self.container.append(value)
        
        
       
    def pop(self):
        '''
        从栈中取走数据
        :return:
        '''
        return self.container.pop()

应用场景:Scrapy框架中,Flask源码的LocalStack对象维护的一个栈
如:
在这里插入图片描述

  • 面向对象的元类metaclass

对象是类()创建的
类是type()创建的,一般定义一个类就写出来了一个类,
但类其实有两种创建方式,平时只用一种:class 类…,另外一种写法:

# class Foo():
#     name = 'Lau'
#
#     def func(self):
#         return 123


Foo = type('Foo', (object, ), {'name': 'Lau', 'func': lambda self:123})  # 和以上创建类效果一样,类是type()创建的

obj = Foo()

ret = obj.func()
print(ret)  #  >>> 123

在这里插入图片描述
只要是class 类名,一定调用了type(),type()又调用object(),type是由object创建的,object是由C创建的,C语言底层的代码开辟了空间,所以object是python类的最底层的代码,主要是用来在内存中开辟一个空间等。

  • metaclass指定类由哪个type创建(继承type的子类),默认为type。
class MyType(type):
    def __init__(self, *args, **kwargs):
        print(args)
        super().__init__(*args, **kwargs)


class Foo(object, metaclass=MyType):  # 实例化时调用了MyType
    name = 'Lau'

    def func(self):
        return 123

运行结果

('Foo', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'Foo', 'name': 'Lau', 'func': <function Foo.func at 0x0000026FA3294840>})
# 类名, 继承, 成员
  • 如果类指定了metaclass为MyType,派生类都是又指定的类MyType创建
class MyType(type):
    def __init__(self, *args, **kwargs):
        print(args)
        super().__init__(*args, **kwargs)


class Foo(metaclass=MyType):
    pass


class Bar(Foo):  # 由于Bar的基类是由MyType创建,所以Bar也是由MyType创建
    pass

运行结果

('Foo', (), {'__module__': '__main__', '__qualname__': 'Foo'})
('Bar', (<class '__main__.Foo'>,), {'__module__': '__main__', '__qualname__': 'Bar'})

指定metaclass的源码:

  • flask中的wtforms

在这里插入图片描述
上图关系:
Form的基类是NewBase,NewBase的基类是BaseForm,且由FormMeta创建,FormMeta本身继承type,所以Form也是由FormMeta创建的。
为什么要用FormMeta创建呢?因为FormMeta中做了一些顺序的操作,内部创建了一个计数器,在代码编译过程中,实例化每个字段时。计数器记录了谁是第一个,谁是第二个, Django1.10前没有这个功能,以便循环时每个字段展示的顺序是代码本来的顺序。
指定metaclass的作用,在类初始化时可以做一些操作,运行脚本时就执行了,类加载时自定义一些代码执行。不用等到实例化时才做。

  • Django的form组件
from django import forms

class LoginForm(forms.Form):
    name = forms.CharField()

在这里插入图片描述

总结:
当一个对象实例化时,执行了创建该对象的__call__方法,当创建一个类时,由于实际上执行了创建该类的类加括号,也就是__call__方法,而该方法中执行了__init__和__new__方法。
如:

class MyType(type):

    def __init__(self, name, bases, dic): # self: Foo,类的初始化,
        print('type.init')
        super().__init__(name, bases, dic)


    def __new__(cls, name, bases, dic):   # 用来开辟空间,创建类,要有返回值,否则会空,cls--->MyType,MyType创建的,创建时的参数要传进去。返回什么其实例化对象就是什么, 如返回123,其对象就是123
        print('type.new')
        return super().__new__(cls, name, bases, dic)# 创建了类并赋值给Foo


    def __call__(self, *args, **kwargs):  # self是Foo, 由于对象加()执行的是__call__方法,本质上__new__方法和__init__方法是__call__触发的
        print(self, args, kwargs)
        print('创建对象')
        ob = self.__new__(self, *args, **kwargs)
        ob.__init__(*args, **kwargs)
        print(ob)


class Foo(object, metaclass=MyType):  # 执行MyType(), 执行MyType的__call__方法(对象Foo调用),由于没有,执行父类type的__call__方法,其__call__方法中执行__new__()(对象Foo调用),__init__()(对象Foo调用)

    def __init__(self):  # 如果该方法没有在创建该类的__call__方法中执行,则不会生效。
        print('init')


    def __new__(cls, *args, **kwargs):
        print(1)
        return super().__new__(cls)


obj = Foo()

运行结果:

type.new
type.init
<class '__main__.Foo'> () {}
创建对象
1
init
<__main__.Foo object at 0x000001A1E018E390>

应用场景:
在类创建前写条日志,类实例化后再写入一条日志,可以在MyType的_init_,_new_,或__call__方法中插入代码。

  • metaclass 相关补充
class Foo:
    def func(self):
        print('foo.func')


obj = Foo() 

obj.func()  # 一般的执行类中的方法方式, 被对象调用时叫方法

Foo.func(obj)   # self默认为类的对象。如果类直接调用其方法时,self没有创建过,可手动传,类调用时叫函数

运行结果

foo.func
foo.func

一般平时不用metaclass,在源码中出现的比较

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值