单分派,多分派

本文通过一个具体的Java示例详细解析了单分派与多分派的概念。介绍了一个父类和子类重写方法的场景,并通过分析字节码指令解释了如何根据接收者和参数类型来确定调用哪个方法。

方法的接收者和方法的参数统称为方法的宗量。 根据分派基于宗量多少(接收者是一个宗量,参数是一个宗量),可以将分派分为单分派和多分派。单分派是指根据一个宗量就可以知道调用目标(即应该调用哪个方法),多分派需要根据多个宗量才能确定调用目标。

如下代码:

package diptch;

public class DynamicDispatch {

    static class QQ {}
    static class _360 {}

    public static class Father {
        public void hardChoice(QQ arg) {
            System.out.println("father choose QQ");
        }

        public void hardChoice(_360 arg) {
            System.out.println("father choose _360");
        }
    }

    public static class Son extends Father {
        @Override
        public void hardChoice(QQ arg) {
            System.out.println("son choose QQ");
        }

        @Override
        public void hardChoice(_360 arg) {
            System.out.println("son choose 360");
        }
    }

    public static void main(String[] args) {
        Father father = new Father();
        Father son = new Son();
        father.hardChoice(new _360());
        son.hardChoice(new QQ());
    }
}

执行结果:
father choose _360
son choose QQ

来看字节码字节码指令具体执行:

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #16                 // class diptch/DynamicDispatch$Fa
ther
         3: dup
         4: invokespecial #18                 // Method diptch/DynamicDispatch$F
ather."<init>":()V
         7: astore_1
         8: new           #19                 // class diptch/DynamicDispatch$So
n
        11: dup
        12: invokespecial #21                 // Method diptch/DynamicDispatch$S
on."<init>":()V
        15: astore_2
        16: aload_1
        17: new           #22                 // class diptch/DynamicDispatch$_3
60
        20: dup
        21: invokespecial #24                 // Method diptch/DynamicDispatch$_
360."<init>":()V
        24: invokevirtual #25                 // Method diptch/DynamicDispatch$F
ather.hardChoice:(Ldiptch/DynamicDispatch$_360;)V
        27: aload_2
        28: new           #29                 // class diptch/DynamicDispatch$QQ

        31: dup
        32: invokespecial #31                 // Method diptch/DynamicDispatch$Q
Q."<init>":()V
        35: invokevirtual #32                 // Method diptch/DynamicDispatch$F
ather.hardChoice:(Ldiptch/DynamicDispatch$QQ;)V
        38: return
      LineNumberTable:
        line 31: 0
        line 32: 8
        line 33: 16
        line 34: 27
        line 35: 38
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      39     0  args   [Ljava/lang/String;
               8      31     1 father   Ldiptch/DynamicDispatch$Father;
              16      23     2   son   Ldiptch/DynamicDispatch$Father;
}

C:\Users\Administrator\js>

首先确定方法的接收者,发现两个对象变量的静态类型都是Father类型的,因此在class文件中写的Father类中方法的符号引用如下。

24: invokevirtual #25  //Methoddiptch/DynamicDispatch$Father.hardChoice(Ldiptch/DynamicDpatch$_360;)V


35: invokevirtual #32                 // Method diptch/DynamicDispatch$F
ather.hardChoice:(Ldiptch/DynamicDispatch$QQ;)V            

两个方法的接受者都是Father 这个静态类型,形参部分的具体类型也是在编译器就已经确定了静态类型,然后各自找各自的方法所以静态类型属于多分派

然后在看son.hardChoice(new QQ());这个方法 son的静态类型是Father,实际类型是Son,准确的说是在执行invokevirtual 这个字节码指令时,他只会关心你的实际类型,而不会去操心你的参数,因为这时候方法形参不管是实际类型,还是静态类型都不会对方法的选择有任何影响。

<think>好的,我现在需要帮助用户理解Python中的`singledispatch`的使用方法原理。首先,用户可能已经知道装饰器的基本概念,但需要更深入的单分派知识。我需要结合他们提供的引用内容,尤其是引用[1],其中有一个具体的示例。 首先,我应该解释`singledispatch`的作用,它用于根据第一个参数的类型来重载函数,类似于其他语言的方法重载。然后,我需要说明如何使用它,比如通过装饰器注册不同类型的处理函数。引用中的例子已经展示了基本的用法,可以引用这个示例来说明。 接下来,关于原理部分,用户可能想知道`singledispatch`是如何在底层实现的。根据引用[2][3],装饰器的原理涉及将函数作为参数传递给装饰器类,并修改其行为。可能`singledispatch`内部维护了一个注册表,存储不同类型对应的处理函数。当调用被装饰的函数时,会根据第一个参数的类型查找对应的实现。 需要注意的是,用户可能混淆了`singledispatch`其他装饰器的用法,比如类装饰器或带参数的装饰器。应该明确说明`singledispatch`是`functools`模块的一部分,专门用于函数重载,而其他装饰器可能有不同的用途,比如添加计时功能(如引用[2]中的例子)。 用户可能还想知道如何扩展这个功能,比如处理更多的类型或者如何设置默认实现。引用[1]中的示例展示了如何处理未注册类型的情况,即抛出`NotImplementedError`,但也可以注册一个默认处理函数。 另外,需要提到`singledispatchmethod`,这是在类中使用的,处理第一个参数是实例方法的情况,比如`self`之后的参数类型。这可能对用户有帮助,如果他们需要在类中使用单分派的话。 最后,总结原理部分,说明`singledispatch`通过装饰器注册表机制实现动态分发,根据参数类型选择对应的函数实现。同时,引用[3]提到的`wraps`可能在这里用于保留原函数的元数据,但`singledispatch`本身可能已经处理了这些细节。 现在需要确保回答结构清晰,分步骤解释使用方法原理,并生成相关问题。还要检查是否符合格式要求,比如数学表达式用$符号,独立公式用$$,以及正确的中文回答。</think>### Python singledispatch 使用与原理详解 #### 一、单分派核心概念 单分派(Single Dispatch)是一种根据**第一个参数类型**动态选择函数实现的多态技术。在Python中通过`functools.singledispatch`实现[^1]。 数学表达示例: 若定义函数$f(x)$,单分派允许针对$x$的不同类型实现不同逻辑: $$f(x) = \begin{cases} g(x) & x \in \text{类型A} \\ h(x) & x \in \text{类型B} \end{cases}$$ #### 二、使用方法分步说明 1. **基础注册** ```python from functools import singledispatch @singledispatch def process(data): raise NotImplementedError("未支持的类型") @process.register(int) def _(data): # 处理整数类型 return f"处理整数: {data}" @process.register(list) def _(data): # 处理列表类型 return f"处理列表: {len(data)}元素" ``` 2. **类型扩展** ```python @process.register(float) def _(data): # 新增浮点数处理 return f"处理浮点数: {data:.2f}" ``` 3. **类型联合(Python 3.10+)** ```python from typing import Union @process.register(Union[str, bytes]) def _(data): # 处理字符串或字节类型 return f"处理文本: {data.decode() if isinstance(data, bytes) else data}" ``` #### 三、底层实现原理 1. **注册表机制** `singledispatch`创建**类型到函数的映射字典**,通过`registry`属性存储: ```python print(process.registry) # 输出: {object: <function...>, int: <function...>, ...} ``` 2. **分发逻辑** 调用过程分解为: $$dispatch(x) = registry.get(type(x), default_impl)$$ $$\text{最终执行} \Rightarrow dispatch(x)(x, ...args)$$ 3. **装饰器链** 原始函数被替换为分发器,注册方法通过`register()`修改注册表: ```mermaid graph LR A[原始函数] --> B[singledispatch装饰器] B --> C[分发函数] C --> D[注册表查询] D --> E[具体实现] ``` #### 四、高级应用场景 1. **数据序列化** ```python @singledispatch def serialize(obj): raise TypeError("未支持的序列化类型") @serialize.register(datetime) def _(obj): return obj.isoformat() @serialize.register(dict) def _(obj): return json.dumps(obj) ``` 2. **科学计算扩展** ```python import numpy as np @process.register(np.ndarray) def _(data): return f"处理矩阵: {data.shape}维度" ``` #### 五、性能优化建议 - 优先注册高频类型 - 对`Union`类型使用`functools.singledispatchmethod`(针对方法) - 避免在热路径中进行类型检查
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值