質問

If I create an object:

class eggs(object):
    """ This wasn't needed """

    def __setattr__(self, name, value):
        print name, value

I can understand that if I do:

class eggs(object):
    """ This wasn't needed """

    def __setattr__(self, name, value):
        print name, value


if __name__ == "__main__":
    foo = eggs()
    foo.bar = 5
    print foo

I get:

bar 5
<__main__.eggs object at 0x8f8bb0c>

However when I do:

if __name__ == "__main__":
    foo = eggs()
    foo = 5
    print foo

I get:

5

My question is what "magic" method is called when foo = 5 is called?

For example I do do:

class eggs(object):
    """ This wasn't needed """
    def __init__(self):
        self.bacon = False

    def __setattr__(self, name, value):
        if not name == "bacon": 
            raise Exception("No way Hosay")
        if value not in [True, False]:
            raise Exception("It wasn't supposed to be like this")
        super(eggs, self).__setattr__(name, value)
        print "Variable became {0}".format(value)




if __name__ == "__main__":
    foo = eggs()
    foo.bacon = True
    foo.bacon = 5 > 4
    foo.bacon = True or False
    # ETC

Which returns:

Variable became False
Variable became True
Variable became True
Variable became True

I want to do that without bacon.

役に立ちましたか?

解決

You've misunderstood the way that variables work in python - they are just a reference to an object, and reassignment just overwrites the reference with another reference.

As a simplified explanation, imagine that variable assignment works like assigning members of a dictionary. If we call that dictionary variables, we could rewrite

foo = 5

as

variables["foo"] = 5

Now, you want the current value of variables["foo"] (if it even exists) to be notified of the assignment, which would mean that the previous statement would become analoguous to the following:

variables["foo"].__somemethod__() #call magic method to notify reassignment
variables["foo"] = 5 # set new value

While you could actually implement this exact behaviour with a dict subclass, this is simply not the way that variable assignment in CPython is implemented. The object that is referenced by a variable is not in any way notified of the fact that it is not referenced by variables["foo"] anymore. All that happens is that the reference count of the previously referenced object (if there was any) is decremented by one.

As the answer by Stick demonstrates, there is a __del__ method which is called when the object is garbage collected, which may or may not be enough for your actual use case. But when this is called is actually up to the gc so it can exhibit some funky behaviour; see e.g. this answer for a discussion of some quirks. Furthermore, before python3.4 it was not even guaranteed that any referenced objects are still alive when __del__ is invoked, this has now been fixed: http://legacy.python.org/dev/peps/pep-0442/.

If you really need to be notified of a reference being overwritten (and I'd doubt that there isn't a better way to solve your actual problem), consider using a subclassed or monkeypatched dict for all references you want to track. You would need to overwrite the __setattr__ method to perform the actions you want to happen when a reference is overwritten.

他のヒント

If you want to be weird you can override __del__ but that's a whole can of worms typically.

>>> class A(object):
...     def __del__(self):
...             print "oh noooo!"
... 
>>> Stick = A()
>>> Stick = 5
oh noooo!
>>> Stick
5
>>> 

EDIT: It sounds like you want to know when the object is created or deleted as well?

Perhaps just a decorator will suffice?

>>> def talk(func):
...     def inner(*args, **kwargs):
...             print "I'm doing things!"
...             return func(*args, **kwargs)
...     return inner
... 
>>> class A(object):
...     @talk
...     def __init__(self):
...             pass
...     @talk
...     def __del__(self):
...             pass
... 
>>> Stick = A()
I'm doing things!
>>> del Stick
I'm doing things!
>>> Stick
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Stick' is not defined
>>>

This works for instantiation and deletion, as well as reassignment of the variable.

And of course -- if you need different methods when __init__ and __del__ are called, you'll want to use different decorators. Just put your "actual work" in the nested function.

def wrapper(func):
    def inner(*args, **kwargs):
        #your code here!
        #your code here as well!
        return func(*args, **kwargs)
    return inner

Just be leery of goofballs and whatnot when mucking around with __del__, but I think this is a relatively sane way to do it.

You should check out Descriptors, they're the closest thing to what you want

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top