有没有一个标准的类,通过调用int(self)来实现所有类似整数的魔术方法?

huangapple go评论59阅读模式
英文:

Is there a standard class that implements all the int-like magic methods by calling int(self)?

问题

请注意:我已经进行了编辑,因为一些回答是根据我的不当措辞提供的。请责备我。感谢大家容忍我。

我正在编写一个具有类似整数的属性的类。

它实现了__int__方法。

我想要实现所有整数具有的魔术方法:__add____and____rshift__等,通过在int(self)上应用相同的函数。

例如:

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

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

  # 还有大约40个其他方法,依此类推。

是否有一个对象可以为我做到这一点?这似乎是一个常见的模式。

我尝试过class MyClass(int),但这不允许我重写__int__

我的类不是整数;我希望它能够被视为整数。

还是我应该以其他方式处理这个问题?

我正在尝试模拟FPGA电线和寄存器 - 值将会动态变化。

例如,如果将inst.wire2定义为inst.wire1 + 10,那么inst.wire2将始终是inst.wire1 + 10;如果inst.wire1发生变化,inst.wire2也会随之变化。

一旦完成,对给定电线或寄存器的几乎所有访问都将是针对其值的访问,因此使每个引用都变成inst.wire1.value(或self.wire1.value)将使代码变得杂乱无章,没有优势。

澄清一下:如果有人将属性视为整数(例如通过将数字添加到它),结果应该是一个常规的、真正的整数,没有任何魔术效果。

英文:

Please note: I have made edits since some of these responses were given, due to my bad wording. Blame me. Thanks to everyone for putting up with me.

I am writing a class that has int-like properties.

It implements the __int__ method.

I'd like to implement all the magic methods that ints have: __add__, __and__, __rshift__ etc., by applying the same function on int(self).

For example:

  def __lshift__(self, other):
    return int(self) &lt;&lt; other

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

  # and so forth, for about 40 more methods.

Is there an object that will do this for me? It seems like this would be a common pattern.

I tried with class MyClass(int), but this did not allow me to override __int__

My class is not an int; I want it to be able to be treated as an int.

Or should I be doing this some other way?

I am trying to simulate FPGA wires & registers - the value will change dynamically.

For example, if inst.wire2 is defined as inst.wire1 + 10, then inst.wire2 will always be inst.wire1 + 10; if inst.wire1 changes, inst.wire2 changes with it.

Once complete, almost all access to a given wire or register will be for it's value, so making every reference inst.wire1.value (or self.wire1.value) will clutter the code immensely, with no advantage.

TO CLARIFY: if someone treats the property as an int (e.g. by adding a number to it), the result should be a regular, real, actual int, with no magic stuff.

答案1

得分: 2

以下是您要翻译的内容:

# 代码部分不要翻译
The approach I would use would be to split your wire class into several related classes: an abstract superclass that implements all the operators, a class that represents an input wire, and two classes that represent binary and unary operators.

Here's a start:

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

此方法允许您构建可以多次评估具有不同输入的电线。

英文:

The approach I would use would be to split your wire class into several related classes: an abstract superclass that implements all the operators, a class that represents an input wire, and two classes that represent binary and unary operators.

Here's a start:

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

class Wire(ABC):
    def __add__(self, other: &#39;Wire | int&#39;) -&gt; &#39;Wire&#39;:
        return BinopWire(self, other, operator.add)

    def __radd__(self, other: &#39;Wire | int&#39;) -&gt; &#39;Wire&#39;:
        return BinopWire(other, self, operator.add)

    # ...

    def __lshift__(self, other: &#39;Wire | int&#39;) -&gt; &#39;Wire&#39;:
        return BinopWire(self, other, operator.lshift)

    def __rlshift__(self, other: &#39;Wire | int&#39;) -&gt; &#39;Wire&#39;:
        return BinopWire(other, self, operator.lshift)

    def __neg__(self) -&gt; &#39;Wire&#39;:
        return UnopWire(self, operator.neg)

    def __inv__(self) -&gt; &#39;Wire&#39;:
        return UnopWire(self, operator.inv)

    @abstractmethod
    def __int__(self) -&gt; int:
        ...

    def __bool__(self) -&gt; bool:
        return bool(int(self))

@dataclass
class InputWire(Wire):
    value: int

    def __int__(self) -&gt; int:
        return self.value

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

    def __int__(self) -&gt; int:
        return self.op(int(self.left), int(self.right))

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

    def __int__(self) -&gt; int:
        return self.op(int(self.arg))


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

This approach allows you to construct wires that can be evaluated multiple times, with different inputs.

答案2

得分: 0

好的,以下是翻译好的部分:

我已经找到了一个答案(不一定是最佳答案,但它有效)。

这有两个部分。

一部分是如何根据需求计算值。这是使用 __get__ 方法完成的。

另一部分是创建一个继承自 __int__ 的类(感谢 @Augusto Vasques 指出这一点!)。

在我的代码中,部分内容如下:

import time

class LikeInt(int):
    # 如果要在 __init__ 中添加参数,
    # 这些相同的参数会传递给 __new__
    # 所以你必须在 __new__ 中忽略它们
    # 这里不需要,因为我没有添加任何参数,
    # 但它会看起来像这样。
    #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 is 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()) # 这只是一个变化的值的示例

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

inst = MyCircuit()

# 这将触发 inst.myfirstwire._get_,从而创建一个 LikeInt。
inst.myfirstwire.something()

# 这将触发 inst.mysecondwire._get_;这将运行函数,然后触发 inst.myfirstwire.__get__
print(inst.mysecondwire + 2)

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

当然,这只是一个巨大的简化;它不包括寄存器等内容,但它满足了问题的条件:

  • inst.myfirstwireinst.mysecondwire 实现了加法/与/右移等操作,而无需逐个编写它们
  • mysecondwire 会始终比 myfirstwire 大一(在任何给定时刻)
  • 你可以 print(inst.mysecondwire + 10)
  • 当你将 inst.mysecondwire 视为 int(例如,通过向其添加数字),结果将是一个常规的、真实的、实际的 int,没有任何神奇的东西。

如果其他人使用了这个,请注意:

英文:

Ok, I've figured out an answer (not necessarily the best one, but it works).

There are two parts to this.

One part is how to calculate the values on demand. This is done using the __get__ method.

The other part is to create a class which inherits from __int__ (thanks @Augusto Vasques for pointing me at that!).

In part, my code reads something like:

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&#39;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(&quot;something!&quot;)

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(&quot;turns out &quot;, self.__function__.__name__, &quot; is &quot;, value, &quot;right now&quot;)

    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)

This is, of course, a massive simplification; it doesn't include register etc., but it meets the criteria of the question:

  • inst.myfirstwire and inst.mysecondwire implement add/and/rshift etc., without me having to code them one-by-one
  • mysecondwire will always be one more than myfirstwire (at any given instant)
  • you can print(inst.mysecondwire + 10)
  • when you treat inst.mysecondwire as an int (e.g. by adding a number to it) the result is a regular, real, actual int, with no magic stuff.

In case someone else uses this,

huangapple
  • 本文由 发表于 2023年5月28日 14:40:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76350255.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定