2015-04-24 4 views
0

Я хотел бы хранить сигнатуру сигнатур функций в базовом классе и иметь дочерние классы, переопределяющие некоторые из этих сигнатур функций. Однако, похоже, что моя реализация ломает полиморфизм. Я хочу несколько предложений по элегантной альтернативе. Благодаря!python: Function pointer breaks polymorphism

class Base: 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     pass 

    def g(self): 
     pass 

    a = 1 
    FXN = { 
     1 : f, 
     2 : g 
    } 

class Child1(Base): 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     print 'hi' 

class Child2(Base): 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     print 'hey' 

    def g(self): 
     print 'hello' 

if __name__ == '__main__': 
    Child1(1) # Nothing happens 
    Child2(1) # Nothing happens 
    Child2(2) # Nothing happens 

Edit: осветленный проблема

Edit 2: осветленный проблема снова

Комментарий: В более сложном случае, каждый из конструкторов может взять в объект некоторого типа. Ключ - это перечисление, которое идентифицирует тип объекта и диктует, какой путь инициирования экземпляра взять.

Решение от Бруно: Мистер Бруно ниже дал мне относительно элегантное решение.

class Base: 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     pass 

    def g(self): 
     pass 

    FXN = { 
     1 : lambda self: self.f(), 
     2 : lambda self: self.g() 
    } 

class Child1(Base): 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     print 'hi' 

class Child2(Base): 
    def __init__(self, key): 
     self.FXN[key](self) 

    def f(self): 
     print 'hey' 

    def g(self): 
     print 'hello' 

if __name__ == '__main__': 
    Child1(1) # hi 
    Child2(1) # hey 
    Child2(2) # hello 
+5

Честность, почему бы вы когда-нибудь хотели бы сделать это вместо того, чтобы используя стандартную нотацию языка? Является ли ваша проблема проблемой [XY] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? –

+0

Моя проблема заключается в том, чтобы получить элегантный способ выполнения однопараметрической перегрузки конструктора. Я не поклонник больших блоков if-elif-else – user1836155

+0

И почему вы думаете, что оригинальный метод Python (и тот, что у большинства других OOPL) не изящный? – Olaf

ответ

1

Это не «указатели на функции», только «функция» - функции Python являются объектами первого класса. И это действительно не сработает, как вы ожидаете, поскольку то, что вы храните в Base.FXN, является локальным (для операторов класса) f и g.

Есть несколько возможных решений, в зависимости от вашего реального прецедента (что вы ничего не говорите о том, и я подозреваю XY problem), один из самых простых являются определением этих функций в виде простых оберток и делегирования осуществление другой функции (которые вы затем переопределить при необходимости в дочерних классах), то есть:

class Base(object): 
    def f(self): 
     return self._f() 

    def _f(self): 
     pass 

    def g(self): 
     return self._g() 

    def _g(self): 
     pass 

    FXN = { 
     1 : f, 
     2 : g 
    } 


class Child1(Base): 
    def _f(self): 
     print 'hi' 

class Child2(Base): 
    def _g(self): 
     print 'hello' 

if __name__ == '__main__': 
    c1 = Child1() 
    c2 = Child2() 
    c1 = Child1.FXN[1](c1) 
    c2 = Child1.FXN[2](c2) 

более сложный (но, возможно, более эффективные, еще раз в зависимости от проблемы реального вы пытаетесь решить) решение может быть основано на декораторах (декораторы классов или декораторы функций), пользовательский дескриптор tors или (и/или ...) пользовательский метакласс.

EDIT: учитывая отредактированный пример, два простых решения:

использование лямбды:

class Base(object): 
    def f(self): 
     pass 

    def g(self): 
     pass 

    FXN = { 
     1 : lambda self: self.f(), 
     2 : lambda self: self.g() 
    } 

for base in receive_some_bases(): 
    base.FXN[1](base) 
    base.FXN[2](base) 

использование getattr():

class Base(object): 
    def f(self): 
     pass 

    def g(self): 
     pass 

    FXN = { 
     1 : "f", 
     2 : "g", 
    } 

for base in receive_some_bases(): 
    getattr(base, base.FXN[1])() 
    getattr(base, base.FXN[2])() 
+0

Спасибо за комментарий, я обновил свой пример, чтобы отразить свою проблему. – user1836155

+0

Мне нравится ваш лямбда-подход. Я попробовал. Похоже, что вам нужна дополнительная() в конце для функции для вызова. Это выглядит немного уродливым со всеми этими скобками, но, скорее всего, лучший ответ я получу далеко. – user1836155

+0

Метод getattr() не поддерживается, потому что нет прямой связи между строкой и методом. Предположим, что один переименовывает метод в Eclipse, изменение не будет автоматически обнаружено. – user1836155

0

FXN не имеет значения, если у вас есть экземпляр объекта

Попробуйте это:

class Base: 
    def f(self): 
     pass 

    def g(self): 
     pass 

class Child1(Base): 
    def f(self): 
     print 'hi' 

class Child2(Base): 
    def g(self): 
     print 'hello' 

if __name__ == '__main__': 
    c1 = Child1() 
    c2 = Child2() 

    c1.f() 
    c2.g() 
+1

Вы получите сообщение об ошибке с 'c1 = Child1.f()', поскольку вы вызываете несвязанный метод 'f' класса, а не экземпляр. Используйте 'c1.f()' или 'Child1.f (c1)'. – paidhima

1

FXN является переменной класса, а не связаны с дочерними классами. Он также ссылается на исходные объекты функции f, g в dict, а не на имена, определенные в дочерних классах. Как это должно быть, поскольку они даже не существуют, когда тело базового класса выполняется?

Почему бы не использовать обычные методы? Они будут связаны с каждым классом. В качестве альтернативы вы можете привязать функции к каждому экземпляру, используя types.MethodType в __init__().

И, помните: все атрибуты экземпляра/класса уже хранятся в типе объекта (__dict__), поэтому на самом деле нет необходимости создавать свои собственные.

+0

Это правда, что __dict__ имеет указатели на методы, но мне бы хотелось, чтобы методы были сопоставлены с определенными ключами по моему выбору. Позвольте мне немного изменить свой сценарий – user1836155

+0

@ user1836155: Этот дикт - это просто дикт. Вы забываете, что Python имеет динамическую типизацию, отличную от Java, C++ и т. Д. Вы можете в принципе добавить атрибут к любому объекту. А в Python есть все ** объект. Поэтому вы можете просто привязать любую функцию к экземпляру с помощью 'type.MethodType' (я исправил свой ответ в этом аспекте, не знаю, почему я написал ClassMethod). – Olaf

1

Это сделает ваш пример сделать то, что вы, кажется, хотят:

class Base: 
    def __init__(self): 
     self.FXN = { 
      1 : self.f, 
      2 : self.g 
     } 
    def f(self): 
     pass 

    def g(self): 
     pass 


def receive_some_bases(): 
    return [Child1(), Child2(), Child1(), Child2(), Child1()] 

if __name__ == '__main__': 
    for base in receive_some_bases(): 
     base.FXN[1]() 
     base.FXN[2]() 

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

FXN={ 
    1: Child1, 
    2: Child2 
} 
def receive_some_integers(): 
    return [1, 2, 1, 2, 1] 

if __name__ == '__main__': 
    for value in receive_some_integers(): 
     c = FXN[value]() # instantiate Childx 
     c.f()    # call a method on instance 
+0

Я это рассмотрел, но тогда у меня будет этот объект для каждого экземпляра вместо класса. Это работает, но я хочу знать, есть ли лучший способ. – user1836155