Python 随机整数行为

Python 随机整数行为,python,int,subclass,metaclass,Python,Int,Subclass,Metaclass,看完之后,我开始思考:是否有可能编写一个行为类似于随机整数的类 我设法用dir()找到了一些可重写的方法: 但是我觉得有一种更优雅的方法可以将randint(1100)注入到每个接受self参数的方法中 有没有一种方法可以做到这一点而不必从头重新编写整个int类 比如: >>> x = RandomInt() >>> x + 1 2 >>> x + 1 74 >>> x * 4 152 一个想法是有一个\uuuu call

看完之后,我开始思考:是否有可能编写一个行为类似于随机整数的类

我设法用
dir()
找到了一些可重写的方法:

但是我觉得有一种更优雅的方法可以将
randint(1100)
注入到每个接受
self
参数的方法中

有没有一种方法可以做到这一点而不必从头重新编写整个
int

比如:

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

一个想法是有一个
\uuuu call\uuu
方法,它返回一个随机数

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

您可以在运行时附加这些方法:

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
这允许您选择随机操作的方法

请注意,您可能会尝试使用或自定义属性的访问方式,并避免在类中显式设置方法,但这对特殊方法不起作用,因为它们会查找

上面的代码有一些问题

正如所建议的,以下操作无法正常工作:

>>> 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')
这是因为Python没有对
int
继承的类型使用
\uuuuuu索引\uuuuu

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;
        }
    }
如您所见,它首先检查
int
long
,然后才尝试调用
\uuuuuuu索引


解决方案是从
对象
继承,从
int
克隆/包装属性,或者实际上我更喜欢,我想它也可以以类似的方式更正。

这是一个不同的答案,因为它与我发布的另一个答案非常不同。 (我觉得这应该分开)

守则:

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 + x
49
>>> x // x
2
>>> x * x
4958
>>> x - x
77
>>> x ** x
467056167777397914441056671494001L
>>> float(x) / float(x)
0.28

还有一个版本,它使用类属性来克服新/旧样式的问题(感谢@gatto):

输出:

>>> x
86
>>> x
22
>>> x * x
5280
>>> [1] * x
[1, 1, 1, 1, 1, 1]
>>> x * '0123'
'0123012301230123'
>>> s[x] # s = '0123456789' * 10
'5'

您是否在谈论动态定义每个函数(在函数列表中的add、mul、subtract等)以返回传递给每个方法的randint?编辑:甚至没有必要要求它在question@jamylak:差不多吧。只要最终结果是,
RandomInt
的“值”对于每个方法都是随机的。@Blender:啊,对于每个方法。是的,那么您需要创建方法。。只有当
int
是左手操作数,或者左手操作数本身没有为操作定义钩子时,这才有效。@Blender
\uuuu repr\uu
只需要
self
,不需要
其他
最短的答案是:
,但随后我收到一条提示:
正文必须至少包含30个字符;您输入了3。
:)…是的,但是您必须编写
x()+1
而不是
x+1
。它不是“hacky”,但不回答问题,因为这不像一个int@Blender不,在用户端仍然是
x+1
。@jamylak为什么@学生:
\u调用
方法有什么作用呢?你不能只返回random.randint(0,100)`你需要用
其他
作为参数来调用基函数。如果我想做
1000+a
这不会work@jamylak我的第一个实现唯一的问题是返回一个普通的
int
,而不是
RandomInt
。我看不出关于
1000+a
的问题在哪里。这是对
\uuuu radd\uuuu
的调用,而不是
\uuuu add\uuuuu
,因此您只需在要添加到类中的方法列表中添加
“\uuuuuu radd\uuuuuuu
。我喜欢在
float
中包装x的方式,因为
x/1.0
失败=)+1个很好的方法(即使键入时间比再次编写整个类要长),我想知道为什么这只适用于老式课程though@Schoolboy我的意思是尝试添加
对象
作为基类,这将使它成为一个newstyle类,但当我尝试时,它没有work@jamylak当我从聚会上回到家的时候,我会尝试一下,Python显然只使用类属性,当一个newstyle类对象传递给BIF时,比如
str
int
和其他。您可以在对
\uuuu int\uuuu
的调用中看到类似于
o->ob\u type->tp\u as\u number->nb\u int(object)
,这意味着
\uu int\uuu
属于一个类型,而不是一个实例。另一方面,旧式类有默认的
nb_int
实现,即属性存在。
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
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'