Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 我可以执行哪些测试以确保正确覆盖了setattr?_Python_Python 3.x_Unit Testing_Setattr - Fatal编程技术网

Python 我可以执行哪些测试以确保正确覆盖了setattr?

Python 我可以执行哪些测试以确保正确覆盖了setattr?,python,python-3.x,unit-testing,setattr,Python,Python 3.x,Unit Testing,Setattr,我学习Python已经有一段时间了,我逐渐明白正确地重写\uuu setattr\uuu可能会很麻烦(!) 有哪些有效方法可以确保/向自己证明已正确执行了超控?我特别关心的是确保覆盖与描述符协议和MRO保持一致 (标记为Python3.x,因为我正在使用它,但这个问题当然也适用于其他版本。) “覆盖”显示默认行为的示例代码(但我如何证明它?): 覆盖违反描述符协议的人为示例(实例存储查找发生在描述符查找之前-但如何测试它?): 理想答案将提供一个通用测试,当\uuuu setattr\uuuu被

我学习Python已经有一段时间了,我逐渐明白正确地重写
\uuu setattr\uuu
可能会很麻烦(!)

有哪些有效方法可以确保/向自己证明已正确执行了超控?我特别关心的是确保覆盖与描述符协议和MRO保持一致

(标记为Python3.x,因为我正在使用它,但这个问题当然也适用于其他版本。)

“覆盖”显示默认行为的示例代码(但我如何证明它?):

覆盖违反描述符协议的人为示例(实例存储查找发生在描述符查找之前-但如何测试它?):


理想答案将提供一个通用测试,当
\uuuu setattr\uuuu
被正确覆盖时,该测试将成功,否则将失败

在本例中,有一个简单的解决方案:添加一个绑定描述符,其名称位于
mydict
中,并测试分配给该名称是否通过描述符(注意:Python 2.x代码,我这里没有安装Python 3):


如果将重写定义为调用父类的
\uuuuuuu setattr\uuuuu
正确,则可以将方法移植到定义其自定义
\uuuuu setattr\uuuuu
的类层次结构中:

def inject_tester_class(cls):
    def __setattr__(self, name, value):
        self._TesterClass__setattr_args.append((name, value))
        super(intermediate, self).__setattr__(name, value)
    def assertSetAttrDelegatedFor(self, name, value):
        assert \
            [args for args in self._TesterClass__setattr_args if args == (name, value)], \
            '__setattr__(name, value) was never delegated'
    body = {
        '__setattr__': __setattr__,
        'assertSetAttrDelegatedFor': assertSetAttrDelegatedFor,
        '_TesterClass__setattr_args': []
    }

    intermediate = type('TesterClass', cls.__bases__, body)
    testclass = type(cls.__name__, (intermediate,), vars(cls).copy())

    # rebind the __class__ closure
    def closure():
        testclass
    osa = testclass.__setattr__
    new_closure = tuple(closure.__closure__[0] if n == '__class__' else c
                        for n, c in zip(osa.__code__.co_freevars, osa.__closure__))
    testclass.__setattr__ = type(osa)(
        osa.__code__, osa.__globals__, osa.__name__,
        osa.__defaults__, new_closure)

    return testclass
此函数跳转几圈以插入一个中间类,该类将拦截任何正确委派的
\uuuu setattr\uuu
调用。即使除了默认的
对象
之外没有任何基类,它也可以工作(这不允许我们替换
\uuuuuuu setattr\uuuuuu
,以便更容易地测试它)

它确实假设您正在使用
super()。\uuuu setattr\uuuu()
进行委托,而您使用的
super()
没有参数。它还假设不涉及任何元类

额外的
\uuuu setattr\uuuuu
以与现有MRO一致的方式注入;额外的中间类被注入到原始类和MRO的其余部分之间,并将
\uuuuu setattr\uuuu
调用向前委派

要在测试中使用它,您需要使用上述函数生成一个新类,创建一个实例,然后在该实例上设置属性:

MyTestClass = inject_tester_class(MyClass)
my_test_instance = MyTestClass()
my_test_instance.foo = 'bar'
my_test_instance.assertSetAttrDelegatedFor('foo', 'bar')

如果未委派设置
foo
,则会引发
AssertionError
异常,该异常被
unittest
测试运行程序记录为测试失败。

您当前正在测试什么?没有什么特别的。这是一个一般性问题。我正在考虑如何做到这一点,但想不出任何方法(例如为属性设置顺序的每个步骤执行一个log或print语句,以显示它们完成的顺序-不确定如何做到这一点)。但我也很没经验。@RickTeachey:对不起,我看错了。问题是没有调用原始重写的
\uuuuu setattr\uuuu
版本。由于从未调用过
object.\uuuuu setattr\uuuuu
,因此也不会调用带有setter的描述符。答案都是很好的解决方案,很难在两者之间进行选择。最后,我选择了Bruno's,只是因为它更具体地解决了这个特殊的问题。我已经搜索过了,但找不到关于绑定描述符的更多信息。“绑定描述符”等同于“数据描述符”吗?如果没有,是否存在“非绑定”描述符?另外:我理解上面的测试,这是证明mro被违反的一种非常聪明的方法。是否有一种通用的方法来证明任何给定的实现都没有违反mro?是的,“绑定描述符”的意思与“数据描述符”相同:一种实现
\uuuuu set\uuu()
的描述符。请注意/在上面的测试中,它与mro(“方法解析顺序”又名查找继承树)几乎没有关系,因为不涉及继承,它是关于绑定描述符的。好的。那我就把我的术语弄混了。
class MyBindingDescriptor(object):
    def __init__(self, key):
        self.key = key

    def __get__(self, obj, cls=None):
        if not obj:
            return self
        return obj.__dict__[self.key]

    def __set__(self, obj, value):
        obj.__dict__[self.key] = value


sentinel = object()

class MyClass(object):
    test = MyBindingDescriptor("test")

    def __init__(self, mydict):
        self.__dict__['mydict'] = mydict
        self.__dict__["test"] = sentinel

    def __setattr__(self, att, val):
        if att in self.mydict:
            self.mydict[att] = val
        else:
            super(MyClass, self).__setattr__(att, val)


# first test our binding descriptor
instance1 = MyClass({})
# sanity check 
assert instance1.test is sentinel, "instance1.test should be sentinel, got '%s' instead" % instance1.test

# this one should pass ok
instance1.test = NotImplemented
assert instance1.test is NotImplemented, "instance1.test should be NotImplemented, got '%s' instead" % instance1.test

# now demonstrate that the current implementation is broken:
instance2 = MyClass({"test":42})
instance2.test = NotImplemented
assert instance2.test is NotImplemented, "instance2.test should be NotImplemented, got '%s' instead" % instance2.test
def inject_tester_class(cls):
    def __setattr__(self, name, value):
        self._TesterClass__setattr_args.append((name, value))
        super(intermediate, self).__setattr__(name, value)
    def assertSetAttrDelegatedFor(self, name, value):
        assert \
            [args for args in self._TesterClass__setattr_args if args == (name, value)], \
            '__setattr__(name, value) was never delegated'
    body = {
        '__setattr__': __setattr__,
        'assertSetAttrDelegatedFor': assertSetAttrDelegatedFor,
        '_TesterClass__setattr_args': []
    }

    intermediate = type('TesterClass', cls.__bases__, body)
    testclass = type(cls.__name__, (intermediate,), vars(cls).copy())

    # rebind the __class__ closure
    def closure():
        testclass
    osa = testclass.__setattr__
    new_closure = tuple(closure.__closure__[0] if n == '__class__' else c
                        for n, c in zip(osa.__code__.co_freevars, osa.__closure__))
    testclass.__setattr__ = type(osa)(
        osa.__code__, osa.__globals__, osa.__name__,
        osa.__defaults__, new_closure)

    return testclass
MyTestClass = inject_tester_class(MyClass)
my_test_instance = MyTestClass()
my_test_instance.foo = 'bar'
my_test_instance.assertSetAttrDelegatedFor('foo', 'bar')