如何从类变量引用静态方法

6

给定以下类

from __future__ import annotations
from typing import ClassVar, Dict, Final
import abc

class Cipher(abc.ABC):
    @abc.abstractmethod
    def encrypt(self, plaintext: str) -> str:
        pass

    @abc.abstractmethod
    def decrypt(self, ciphertext: str) -> str:
        pass

class VigenereCipher(Cipher):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]

    _TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})

编译失败 (使用3.8.0版本)

../cipher.py:19: in <module>
    class VigenereCipher(Cipher):
../cipher.py:24: in VigenereCipher
    _TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})
../cipher.py:24: in <setcomp>
    _TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})
E   NameError: name 'rotate' is not defined

然而,根据这篇帖子所说,rotate应该是可解析的。需要注意的是,由于我们正在定义它,所以使用类名VigenereCipher来限定也不起作用,因为它找不到VigenereCipher(这是有道理的)。

我可以将rotate设置为模块级别的方法,并且可以工作,但我真的不想这样做,因为它只在VigenereCipher中需要。

还尝试了这个答案,但没有成功。

实际代码在这里。单元测试在这里


评论不适合进行长时间的讨论;此对话已被移至聊天室 - Samuel Liew
1个回答

4
错误发生在这里:
_TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})

您正在尝试引用位于类命名空间中的变量rotate。然而,Python推导式有自己的作用域,并且没有简单的方法将其与类命名空间连接起来。在推导式评估时,没有闭包或全局变量rotate - 因此会触发NameError。上面的代码等同于您的代码:

def _create_TABLE():
    d = {}
    for i in range(26):
        d[chr(i + ord("A"))] = rotate(i) # -> NameError('rotate')
    return d
_TABLE: Final[ClassVar[Dict[str, str]]] = dict(_create_TABLE())
del _create_TABLE

如何从类变量引用静态方法

在Python中,类变量是某些对象,因此它可以引用程序中的任何对象。以下是一些惯用语:

方法1:

class VigenereCipher(Cipher):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]

    _TABLE: Final[ClassVar[Dict[str, str]]]

VigenereCipher._TABLE = {chr(i + ord("A")): VigenereCipher.rotate(i) for i in range(26)}

方案二:

class VigenereCipher(Cipher):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]

    _TABLE: Final[ClassVar[Dict[str, str]]] = (
        lambda r=rotate.__func__: {chr(i + ord("A")): r(i) for i in range(26)})()

方法三:

class VigenereCipher(Cipher):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]

    _TABLE: Final[ClassVar[Dict[str, str]]] = dict(zip(
        (chr(i + ord("A")) for i in range(26)),
        map(rotate.__func__, range(26)),
    ))

方法四:

class VigenereCipher(Cipher):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]

    _TABLE: Final[ClassVar[Dict[str, str]]] = {
        chr(i + ord("A")): r(i) for r in (rotate.__func__,) for i in range(26)}

还有以下基于的方法:

您可以在相关主题中找到更详细的答案(链接)


创建一个方法然后删除它似乎像是一个巨大的黑客行为。另外,在你的代码中,rotate仍然是一个静态方法吗? - Abhijit Sarkar
1
这很可能无法通过任何类型检查,例如Final类型应在定义时初始化,不应重新定义。 - AChampion
我总是检查我发布的内容。看起来你无法客观地检查它。 - facehugger
好的,在一些调整之后,这段代码确实可以工作。我会接受你的答案。 - Abhijit Sarkar
为什么这看起来像是一个hack的解决方案? - AMC
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接