Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reference static method from class variable [duplicate]

Given the class

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)})

Compilation fails (using 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

However, according to this post, rotate should be resolvable. Note that qualifying with class name VigenereCipher doesn't work either since it can't find VigenereCipher (makes sense, since we are in the process of defining it).

I can make rotate a module-level method, and that works, but I don't really want to since it only is needed in VigenereCipher.

Also tried this answer with no success.

Actual code is here. Unit test is here.

like image 960
Abhijit Sarkar Avatar asked Dec 25 '19 00:12

Abhijit Sarkar


People also ask

Can you refer static method through class reference?

You can refer to a static method defined in the class using method references.

How do you create a static reference from a non-static method?

i.e. referring a variable using static reference implies to referring using the class name. But, to access instance variables it is a must to create an object, these are not available in the memory, before instantiation. Therefore, you cannot make static reference to non-static fields(variables) in Java.

Can we have 2 static methods in a class?

The answer is 'Yes'. We can have two or more static methods with the same name, but differences in input parameters. For example, consider the following Java program.

Can static methods reference instance variables?

Static methods can't access instance methods and instance variables directly. They must use reference to object. And static method can't use this keyword as there is no instance for 'this' to refer to.


1 Answers

The error is raised from here:

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

You are trying to refer the variable rotate that is located in class namespace. However python comprehensions have their own scope and there is no simple way to connect it with class namespace. There is no closure or global variable rotate at the moment of comprehension evaluation - thus NameError is invoked. The code above is equal to your code:

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

How to reference static method from class variable

A class variable in python is some object, so it can refer to any objects in your program. Here some idioms you can follow:

Approach 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)}

Approach 2:

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)})()

Approach 3:

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)),
    ))

Approach 4:

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)}

There are also approaches based on:

  • locals function;
  • metaclasses;
  • __init__subclass__ method;
  • descriptor's __set_name__;
  • stack frames;
  • using global keyword.

You can find a more detailed answers in the related topic

like image 101
facehugger Avatar answered Oct 16 '22 12:10

facehugger