Можно ли переписать «Я», чтобы указать на другой объект внутри self.method в Python? - PullRequest
5 голосов
/ 29 октября 2011
class Wrapper(object):
    def __init__(self, o):
        # get wrapped object and do something with it
        self.o = o
    def fun(self, *args, **kwargs):
        self = self.o # here want to swap
        # or some low level C api like
        # some_assign(self, self.o)
        # so that it swaps id() mem addr to self.o
        return self.fun(*args, **kwargs) # and now it's class A

class A(object):
    def fun(self):
        return 'A.fun'

a = A()
w = Wrapper(a)
print type(w) # wrapper
print w.fun() # some operation after which I want to loose Wrapper
print a is w # this goes False and I'd like True :) 
             # so that this is the original A() object 

Есть ли способ сделать это в Python?

Ответы [ 3 ]

11 голосов
/ 29 октября 2011

Присваивание self внутри метода просто связывает локальную переменную self с новым объектом.Как правило, присвоение пустого имени никогда не меняет никаких объектов, оно просто повторяет привязку имени слева, чтобы указать на объект справа.

Так что вам нужно сделать, этоизмените объект self указывает, чтобы соответствовать объекту self.o указывает.Это возможно, только если A и Wrapper являются классами нового стиля и ни один из них не определяет __slots__:

self.__class__ = self.o.__class__
self.__dict__ = self.o.__dict__

Это будет работать в CPython, но я не уверен насчетдругая реализация Python.И даже в CPython это ужасная идея.

(Обратите внимание, что условие is в последней строке вашего кода все равно будет False, но я думаю, что это именно то, что вы намерены.)

4 голосов
/ 29 октября 2011

Нет, вы не можете. Это потребует передачи по ссылке. Вы можете изменить локальную переменную (точнее, параметр) self просто отлично, но это не повлияет на то, из какого места поступила ссылка, переданная в качестве аргумента (например, w).

И учитывая обстоятельства (неявно переданные self), даже невозможно применить обычные хаки (такие как использование одноэлементного списка и изменение x[0]). Даже если такие уловки сработают (или если есть еще более неясный хак, который может сделать это), они будут крайне обескуражены. Это противоречит всему, к чему привыкли программисты Python. Просто заставьте Wrapper объект действовать так, как если бы он был заменен (то есть вперед все до self.o). Это не сделает проверку личности успешной, но это, безусловно, самое простое, чистое и удобное в обслуживании решение.

Примечание. Для экспериментов существует нестандартное и абсолютно непереносимое расширение PyPy, которое может сделать это (полностью заменив объект ): __pypy__.become. Само собой разумеется, это было бы чрезвычайно опрометчиво использовать это. Найдите другое решение.

0 голосов
/ 29 октября 2011

Вы уже перезаписываете себя, но изменение действительно только локально в пределах fun () .Я добавил пару распечаток, чтобы попробовать это:

    def fun(self, *args, **kwargs):
    print self, self.o
    self = self.o # here want to swap
    print self, self.o

В выводе показаны оригинальные Wrapper и A в self и self.o, затем A и traceback, поскольку self.o недопустим в классе A.

<__main__.Wrapper object at 0xb77be38c> <__main__.A object at 0xb77be34c>
<__main__.A object at 0xb77be34c>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/python-2803VQb.py", line 21, in <module>
    print w.fun() # some operation after which I want to loose Wrapper
  File "/tmp/python-2803VQb.py", line 8, in fun
    print self, self.o
AttributeError: 'A' object has no attribute 'o'

Изменение себя и побочных эффектов плохо.Почему бы не назначить w непосредственно в коде вызова:

print w.fun() # some operation after which I want to loose Wrapper
w = a         # Sure you will loose the Wrapper
print a is w # this goes False and I'd like True :) 
...