Как я могу просто наследовать методы от существующего экземпляра? - PullRequest
1 голос
/ 01 сентября 2008

Ниже у меня есть очень простой пример того, что я пытаюсь сделать. Я хочу иметь возможность использовать HTMLDecorator с любым другим классом. Не обращайте внимания на то, что это называется декоратор, это просто имя.

import cgi

class ClassX(object):
  pass # ... with own __repr__

class ClassY(object):
  pass # ... with own __repr__

inst_x=ClassX()

inst_y=ClassY()

inst_z=[ i*i for i in range(25) ]

inst_b=True

class HTMLDecorator(object):
   def html(self): # an "enhanced" version of __repr__
       return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))

print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_y).html()
wrapped_z = HTMLDecorator(inst_z)
inst_z[0] += 70
wrapped_z[0] += 71
print wrapped_z.html()
print HTMLDecorator(inst_b).html()

Выход:

Traceback (most recent call last):
  File "html.py", line 21, in 
    print HTMLDecorator(inst_x).html()
TypeError: default __new__ takes no parameters

Возможно ли то, что я пытаюсь сделать? Если так, что я делаю не так?

Ответы [ 6 ]

2 голосов
/ 01 сентября 2008

Оба решения Джона будут работать. Другой вариант, который позволяет HTMLDecorator оставаться очень простым и чистым, состоит в том, чтобы обезопасить его как базовый класс. Это также работает только для пользовательских классов, а не встроенных типов:

import cgi

class ClassX(object):
    pass # ... with own __repr__

class ClassY(object):
    pass # ... with own __repr__

inst_x=ClassX()
inst_y=ClassY()

class HTMLDecorator:
    def html(self): # an "enhanced" version of __repr__
        return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))

ClassX.__bases__ += (HTMLDecorator,)
ClassY.__bases__ += (HTMLDecorator,)

print inst_x.html()
print inst_y.html()

Тем не менее, будьте осторожны - такие исправления обезьяны обходятся дорого за удобочитаемость и удобство сопровождения вашего кода. Когда вы вернетесь к этому коду год спустя, может стать очень сложно выяснить, как ваш ClassX получил этот метод html (), особенно если ClassX определен в какой-то другой библиотеке.

2 голосов
/ 01 сентября 2008

Очень близко, но потом я теряю все от ClassX. Ниже приведено кое-что, что дал мне коллега, но он отвратителен. Должен быть лучший способ.

Похоже, вы пытаетесь настроить какую-то схему прокси-объекта. Это выполнимо, и есть лучшие решения, чем у вашего коллеги, но сначала подумайте, будет ли проще просто установить некоторые дополнительные методы. Это не будет работать для встроенных классов, таких как bool, но будет работать для ваших пользовательских классов:

def HTMLDecorator (obj):
    def html ():
        sep = cgi.escape (repr (obj))
        return sep.join (("<H1>", "</H1>"))
    obj.html = html
    return obj

А вот и версия прокси:

class HTMLDecorator(object):
    def __init__ (self, wrapped):
        self.__wrapped = wrapped

    def html (self):
        sep = cgi.escape (repr (self.__wrapped))
        return sep.join (("<H1>", "</H1>"))

    def __getattr__ (self, name):
        return getattr (self.__wrapped, name)

    def __setattr__ (self, name, value):
        if not name.startswith ('_HTMLDecorator__'):
            setattr (self.__wrapped, name, value)
            return
        super (HTMLDecorator, self).__setattr__ (name, value)

    def __delattr__ (self, name):
        delattr (self.__wraped, name)
0 голосов
/ 01 сентября 2008

@ Джон (37479):

Очень близко, но потом я теряю все от ClassX. Ниже приведено кое-что, что дал мне коллега, но он отвратителен. Должен быть лучший способ.

import cgi
from math import sqrt

class ClassX(object): 
  def __repr__(self): 
    return "Best Guess"

class ClassY(object):
  pass # ... with own __repr__

inst_x=ClassX()

inst_y=ClassY()

inst_z=[ i*i for i in range(25) ]

inst_b=True

avoid="__class__ __init__ __dict__ __weakref__"

class HTMLDecorator(object):
    def __init__(self,master):
        self.master = master
        for attr in dir(self.master):
            if ( not attr.startswith("__") or 
                attr not in avoid.split() and "attr" not in attr):
                self.__setattr__(attr, self.master.__getattribute__(attr))

    def html(self): # an "enhanced" version of __repr__
        return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))

    def length(self):
        return sqrt(sum(self.__iter__()))

print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_y).html()
wrapped_z = HTMLDecorator(inst_z)
print wrapped_z.length()
inst_z[0] += 70
#wrapped_z[0] += 71
wrapped_z.__setitem__(0,wrapped_z.__getitem__(0)+ 71)
print wrapped_z.html()
print HTMLDecorator(inst_b).html()

Выход:

<H1>Best Guess</H1>
<H1><__main__.ClassY object at 0x891df0c></H1>
70.0
<H1>[141, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576]</H1>
<H1>True</H1>
0 голосов
/ 01 сентября 2008

Ах, в таком случае, может быть, такой код будет полезен? На самом деле он не имеет ничего общего с декораторами, но демонстрирует, как передать аргументы в функцию инициализации класса и получить эти аргументы для дальнейшего использования.

import cgi

class ClassX(object):
    def __repr__ (self):
        return "<class X>"

class HTMLDecorator(object):
    def __init__ (self, wrapped):
        self.__wrapped = wrapped

    def html (self):
        sep = cgi.escape (repr (self.__wrapped))
        return sep.join (("<H1>", "</H1>"))

inst_x=ClassX()
inst_b=True

print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_b).html()
0 голосов
/ 01 сентября 2008

@ Джон (37448):

Извините, возможно, я ввел вас в заблуждение по поводу имени (плохой выбор). Я на самом деле не ищу функцию декоратора или что-то общее с декораторами. Я хочу, чтобы html (self) def использовал ClassX или ClassY __repr__. Я хочу, чтобы это работало без изменения ClassX или ClassY.

0 голосов
/ 01 сентября 2008

Возможно ли то, что я пытаюсь сделать? Если так, что я делаю не так?

Это, конечно, возможно. Что плохого в том, что HTMLDecorator.__init__() не принимает параметры.

Вот простой пример:

def decorator (func):
    def new_func ():
        return "new_func %s" % func ()
    return new_func

@decorator
def a ():
    return "a"

def b ():
    return "b"

print a() # new_func a
print decorator (b)() # new_func b
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...