7

この質問を見た後、疑問に思い始めました:ランダムな整数のように振る舞うクラスを書くことは可能ですか?

私はいくつかのオーバーライド可能なメソッドを見つけることができましたdir():

class RandomInt(int):
    def __add__(self, other):
        return randint(1, 100) + other

    def __mul__(self, other):
        return randint(1, 100) * other

    def __div__(self, other):
        return randint(1, 100) / other

    def __sub__(self, other):
        return randint(1, 100) - other

    def __repr__(self):
        return str(randint(1, 100))

しかし、引数randint(1, 100)を受け入れる各メソッドに注入するよりエレガントな方法があるように感じます。self

intクラス全体を最初から書き直さずにこれを行う方法はありますか?

何かのようなもの:

>>> x = RandomInt()
>>> x + 1
2
>>> x + 1
74
>>> x * 4
152
4

4 に答える 4

2

これは、私が投稿した他の回答とは非常に異なるため、別の回答です。(そして、これは別のものにするに値すると感じました)

コード:

class RandomInt:
    def __getattr__(self, name):
        attr = getattr(int, name, '')
        if attr != '':
            def wrapper(*args, **kw):
                return attr(random.randint(1, 100), *args, **kw)
            return wrapper
        else:
            raise AttributeError(
                    "'{0}' object has no attribute '{1}'".format('RandomInt',name))

実行例:

>>> x = RandomInt()
>>> x
88
>>> 1 + x # __radd__
67
>>> x*100 # __mul__
1900
>>> x+5 # __add__
50
>>> x-1000 # __sub__
-945
>>> x//5 # __floordiv__
8
>>> float(x) # __float__
63.0
>>> str(x) # __str__
'75'
>>> complex(x) # __complex__
(24+0j)
>>> sum([x]*10)
573

改善の余地があります:

>>> x + x

Traceback (most recent call last):
  File "<pyshell#1456>", line 1, in <module>
    x + x
TypeError: unsupported operand type(s) for +: 'instance' and 'instance'

x*xx/x、および同様の


今回は@gattoの回答に似た別のバージョン:

import random, inspect

class RandomInt:
    def __init__(self):
        def inject(attr):
            def wrapper(*args, **kw):
                args = list(args)
                for i,x in enumerate(args):
                    if isinstance(x, RandomInt):
                        args[i] = x+0
                return attr(random.randint(1,100), *args, **kw)
            return wrapper

        for name in dir(int):
            attr = getattr(int, name)
            if inspect.ismethoddescriptor(attr):
                setattr(self, name, inject(attr))

そして、これは以下をサポートしています:

>>> x + x
49
>>> x // x
2
>>> x * x
4958
>>> x - x
77
>>> x ** x
467056167777397914441056671494001L
>>> float(x) / float(x)
0.28

新しいスタイル/古いスタイルの問題を克服するためにクラス属性を使用するさらに別のバージョン (@gatto に感謝):

import random, inspect

class RandomInt(object):
    pass

def inject(attr):
    def wrapper(*args, **kw):
        args = list(args)
        for i,x in enumerate(args):
            if isinstance(x, RandomInt):
                args[i] = random.randint(1,100)
        return attr(*args, **kw)
    return wrapper

for name in dir(int):
    attr = getattr(int, name)
    if inspect.ismethoddescriptor(attr):
        setattr(RandomInt, name, inject(attr))

出力:

>>> x
86
>>> x
22
>>> x * x
5280
>>> [1] * x
[1, 1, 1, 1, 1, 1]
>>> x * '0123'
'0123012301230123'
>>> s[x] # s = '0123456789' * 10
'5'
于 2013-04-27T15:24:33.133 に答える
1
import inspect
from random import randint

class SelfInjecter(type):
    def __new__(self, *args, **kw):
        cls = type(*args, **kw)
        factory = cls.__factory__

        def inject(attr):
            def wrapper(self, *args, **kw):
                return attr(factory(self), *args, **kw)
            return wrapper

        for name in dir(cls):
            attr = getattr(cls, name)

            if inspect.ismethoddescriptor(attr):
                setattr(cls, name, inject(attr))

        return cls

class RandomInt(int):
    __metaclass__ = SelfInjecter
    __factory__ = lambda self: randint(1, 100)

x = RandomInt()
print x + 3, x - 3, x * 3, repr(x)

上記のコードにはいくつかの問題があります。

Schoolboyによって提案されたように、以下は正しく動作しません:

>>> print x * x
0

RandomInt可能であれば、すべての引数を新しい型に変換する必要があります。

def factory(x):
    if isinstance(x, cls):
        return cls.__factory__(x)
    return x

def inject(attr):
    def wrapper(*args, **kw):
        args = [factory(x) for x in args]
        kw = {k: factory(v) for k, v in kw}
        return attr(*args, **kw)

    return wrapper

また、シーケンスの乗算とインデックス作成が期待どおりに機能しません。

>>> [1] * x, x * '123', '123'[x]
([], '', '1')

__index__これは、Python がfor int-inherited 型を使用しないためです。

class Int(int):
    def __index__(self):
        return 2

>>> x = Int(1)
>>> '012'[x], '012'[x.__index__()]
('1', '2')

Python 2.7.4 実装のコードは次のとおりです。

/* Return a Python Int or Long from the object item
   Raise TypeError if the result is not an int-or-long
   or if the object cannot be interpreted as an index.
*/
PyObject *
PyNumber_Index(PyObject *item)
{
    PyObject *result = NULL;
    if (item == NULL)
        return null_error();
    if (PyInt_Check(item) || PyLong_Check(item)) {
        Py_INCREF(item);
        return item;
    }
    if (PyIndex_Check(item)) {
        result = item->ob_type->tp_as_number->nb_index(item);
        if (result &&
            !PyInt_Check(result) && !PyLong_Check(result)) {
            PyErr_Format(PyExc_TypeError,
                         "__index__ returned non-(int,long) " \
                         "(type %.200s)",
                         result->ob_type->tp_name);
            Py_DECREF(result);
            return NULL;
        }
    }

ご覧のとおり、最初に and をチェックしてintからlong、 を呼び出そうとします__index__

解決策はから継承し、からobject属性を複製/ラップすることですint。または、実際にはSchoolboysの答えがもっと好きです。同様の方法で修正できると思います。

于 2013-04-27T14:41:34.007 に答える
0

実行時にメソッドをアタッチできます。

def add_methods(*names):
    def the_decorator(cls):
        for name in names:
            def the_function(self, other):
                 return cls(random.randint(0, 100))
            setattr(cls, name, the_function)
        return cls
    return the_decorator


@add_methods('__add__', '__mul__', '__sub__')
class RandomInt(int):
    pass

これにより、ランダムに動作するメソッドを選択できます。

__getattr__次のようなものを使用したり__getattribute__、属性へのアクセス方法をカスタマイズしたり、クラスでメソッドを明示的に設定したりすることを避けたいと思うかもしれませんが、特別なメソッドでは機能しません。それらのルックアップは属性アクセス メソッドを通過しないためです。 .

于 2013-04-27T14:34:54.280 に答える
0

__call__1つのアイデアは、乱数を返すメソッドを持つことです。

class RandomInt(int):
    def __call__(self):
        return random.randint(1, 100)
    def __add__(self, other):
        return self() + other

    def __mul__(self, other):
        return self() * other

    def __div__(self, other):
        return self() / other

    def __sub__(self, other):
        return self() - other

    def __repr__(self):
        return str(self())

実行例

>>> x = RandomInt()
>>> x * 3
81
>>> x + 3
56
>>> x - 4
68
>>> x / 4
2
于 2013-04-27T14:25:02.720 に答える