python 是否有一个标准类通过调用int(self)来实现所有类似int的神奇方法?

oalqel3c  于 2023-06-04  发布在  Python
关注(0)|答案(2)|浏览(119)

请注意:由于我的措辞不好,我已经对其中一些回复进行了编辑。怪我吧。谢谢大家容忍我。
我正在编写一个类,它具有类似int的属性。
它实现了__int__方法。
我想实现所有int的神奇方法:__add____and____rshift__等,通过对int(self)应用相同的函数。
例如:

def __lshift__(self, other):
    return int(self) << other

  def __bool__(self):
    return bool(int(self))

  # and so forth, for about 40 more methods.

有没有什么东西可以帮我做到这一点?这似乎是一种常见的模式。
我尝试使用class MyClass(int),但这不允许我覆盖__int__
我的类 * 不是 * 一个int;我希望它能被当作一个int。
或者我应该用别的方法来做这件事?
我试图模拟FPGA线&寄存器-值将动态变化。
例如,如果将inst.wire2定义为inst.wire1 + 10,则inst.wire2将始终为inst.wire1 + 10;如果inst.wire1改变,inst.wire2也随之改变。
一旦完成,几乎所有对给定线或寄存器的访问都将是针对其值,因此使每个引用inst.wire1.value(或self.wire1.value)将使代码非常混乱,没有任何好处。
澄清一下:如果有人将属性视为int(例如通过向它添加一个数字),结果应该是一个常规的,真实的,实际的int,没有魔法的东西。

bihw5rsg

bihw5rsg1#

我使用的方法是将wire类拆分为几个相关的类:一个实现所有运算符的抽象超类,一个表示输入线的类,以及两个表示二元和一元运算符的类。
这是一个开始:

from dataclasses import dataclass
from abc import ABC, abstractmethod
from collections.abc import Callable
import operator

class Wire(ABC):
    def __add__(self, other: 'Wire | int') -> 'Wire':
        return BinopWire(self, other, operator.add)

    def __radd__(self, other: 'Wire | int') -> 'Wire':
        return BinopWire(other, self, operator.add)

    # ...

    def __lshift__(self, other: 'Wire | int') -> 'Wire':
        return BinopWire(self, other, operator.lshift)

    def __rlshift__(self, other: 'Wire | int') -> 'Wire':
        return BinopWire(other, self, operator.lshift)

    def __neg__(self) -> 'Wire':
        return UnopWire(self, operator.neg)

    def __inv__(self) -> 'Wire':
        return UnopWire(self, operator.inv)

    @abstractmethod
    def __int__(self) -> int:
        ...

    def __bool__(self) -> bool:
        return bool(int(self))

@dataclass
class InputWire(Wire):
    value: int

    def __int__(self) -> int:
        return self.value

@dataclass
class BinopWire(Wire):
    left: Wire | int
    right: Wire | int
    op: Callable[[int, int], int]

    def __int__(self) -> int:
        return self.op(int(self.left), int(self.right))

@dataclass
class UnopWire(Wire):
    arg: Wire | int
    op: Callable[[int], int]

    def __int__(self) -> int:
        return self.op(int(self.arg))

wire1 = InputWire(7)
wire2 = wire1 + 7
print(int(wire2))
wire3 = -wire2
wire1.value = 3
print(int(wire3))

这种方法允许您构建可以使用不同输入多次评估的关联。

u4dcyp6a

u4dcyp6a2#

好吧,我已经想出了一个答案(不一定是最好的,但它的工作)。
这个问题有两个部分。
其中一部分是如何根据需要计算值。这是使用__get__方法完成的。
另一部分是创建一个从__int__继承的类(感谢@Augusto Vasques指出这一点!).
在某种程度上,我的代码读起来像这样:

import time

class LikeInt(int):
    # if you want to add arguments to __init__,
    # these same arguments are passed to __new__
    # so you have to ignore them in __new__
    # not needed here, as I'm not adding any arguments,
    # but it would look like this.
    #def __new__(cls, x, *args, **kwargs):
    #  return int.__new__(cls, x)

    def something(self):
        print("something!")

class Wire(object):
  def __init__(self, function):
    self.__function__ = function

  def __get__(self, pinstance = None, pclass = None):
    if pinstance == None:
      raise AttributeError()

    value = self.__function__(pinstance)
    print("turns out ", self.__function__.__name__, " is ", value, "right now")

    return LikeInt(value)

def AutoDecorator(Class, **kwargs):
  def AutoDecorate(function):
    return Class(function = function, **kwargs)
  return AutoDecorate

class MyCircuit(object):
  @AutoDecorator(Class = Wire)
  def myfirstwire(self):
    return int(time.time()) # this is just an example of a changing value

  @AutoDecorator(Class = Wire)
  def mysecondwire(self):
    return self.myfirstwire + 1

inst = MyCircuit()

# this will trigger inst.myfirstwire._get_, which creates a LikeInt.
inst.myfirstwire.something()

# this will trigger inst.mysecondwire._get_; this will run the function, which then triggers inst.myfirstwire.__get__
print(inst.mysecondwire + 2)

time.sleep(5)
print(inst.mysecondwire + 2)

当然,这是一个巨大的简化;不包括register等,但符合问题的标准:

  • mysfirstwire和inst.mysecondwire实现了add/和/rshift等功能,而我不必逐个编写它们
  • mysecondwire总是比myfirstwire多一个(在任何给定的时刻)
  • 你可以print(inst.mysecondwire + 10)
  • 当你把inst.mysecondwire当作一个int(例如通过向它添加一个数字),结果是一个常规的,真实的,实际的int,没有魔法的东西。

以防别人用这个,

相关问题