Прототипное программирование в Python - PullRequest
9 голосов
/ 07 января 2011

Javascript использует модель на основе прототипа для своих объектов.Тем не менее, язык очень гибкий, и в несколько строк легко написать функции, которые заменяют другие виды конструкций.Например, можно создать функцию class, эмулирующую стандартное поведение класса, включая наследование или закрытые члены.Или можно имитировать функциональные инструменты, написав, например, функцию curry, которая будет принимать функцию и некоторые ее аргументы и возвращать частично примененную функцию.

Мне было интересно, возможно ли сделатьповернуть вспять и подражать прототипу в более классических языках.В частности, я немного подумал, возможно ли имитировать прототипы в Python, но отсутствие поддержки анонимных функций (более общих, чем лямбда-выражения) застревает.

Можно ли написатьнекоторые функции для имитации пропотипов в языках классов, в частности в Python?

EDIT Позвольте мне привести пример того, как можно реализовать такую ​​вещь (но яя на самом деле не в состоянии сделать все это).

Во-первых, то, что больше всего напоминает объекты Javascript, это словарь Python.Таким образом, мы могли бы иметь простые объекты, такие как

foo = {
    'bar': 1,
    'foobar': 2
}

Конечно, мы хотим добавить метод, и это не проблема, если метод помещается в лямбду

foo = {
    'bar': 1,
    'foobar': 2,
    'method': lambda x: x**2
}

Так что теперьмы можем вызвать, например,

foo['method'](2)
>>> 4

Теперь , если , у нас были произвольные функции в качестве методов, которые мы могли бы продолжать следующим образом.Сначала нам нужно, чтобы функции внутри foo имели доступ к самому foo;в противном случае это просто обычные функции, а не методы.

Полагаю, можно сделать это, применив функцию makeObject к foo, которая перебирает значения foo и всякий раз, когда находит вызываемое значение, изменяет свой атрибут __call__, передавая foo в качестве первого аргумента.

На этом этапе у нас будут автономные объекты, которые могут быть объявлены без необходимости создания классов.

Затем нам нужно предоставить foo прототип, который можно передать в качестве второго аргумента функции makeObject.Функция должна изменить foo.__getattr__ и foo.__setattr__ следующим образом: всякий раз, когда атрибут не найден в foo, его следует искать в foo.prototype.

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

Ответы [ 5 ]

7 голосов
/ 07 января 2011

В Python это намного проще, чем в JS.Ваш код JS может быть заменен следующим:и у них будет доступ к самому foo, без проблем:

>>> def mymethod(self, bar, foobar):
...     self.bar = bar
...     self.foobar = foobar
>>> foo.mymethod = mymethod
>>> foo.mymethod(1,2)
>>> foo.bar
1
>>> foo.foobar
2

Или, если на то пошло:

>>> mymethod(foo, 3, 4)
>>> foo.bar
3
>>> foo.foobar
4

То же самое.Ваш пример в Python смехотворно прост.Вопрос почему .:) Я имею в виду, это было бы лучше:

>>> class Foo(object):
...     def __init__(self, bar, foobar):
...         self.bar = bar
...         self.foobar = foobar
...     def method(self, x):
...         return x**2
4 голосов
/ 07 января 2011

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

...
def __getattr__(self, name):
    if hasattr(prototype, name)
        return getattr(prototype, name)
    else: 
        raise AttributeError

Также обратите внимание на этот вопрос, поскольку в нем есть некоторые заметки о старых и новых объектах стиля.

1 голос
/ 04 января 2016

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

import types
import inspect

class Proto(object):
  def __new__(self, proto, *args, **kw):
    return super(Proto, self).__new__(self, *args, **kw)
  def __init__(self, proto, *args, **kw):
    self.proto = proto
    super(Proto, self).__init__(*args, **kw)
  def __getattr__(self, name):
    try:
      attr = getattr(self.proto, name)
      # key trick: rebind methods from the prototype to the current object
      if (inspect.ismethod(attr) and attr.__self__ is self.proto):
        attr = types.MethodType(attr.__func__, self)
      return attr
    except AttributeError:
      return super(Proto, self).__getattr__(name)

Это должно полностью реализовать наследование прототипа, насколько я понимаю. Одно ограничение заключается в том, что классы, наследуемые от Proto, должны сначала иметь Proto в своих MRO, потому что __new__ и __init__ имеют прототип в качестве первого параметра, который они отбрасывают при делегировании на super. Вот пример использования:

from prototypal import Proto

class A(Proto):
  x = "This is X"
  def getY(self):
    return self._y

class B(Proto):
  _y = "This is Y"

class C(object):
  def __getattr__(self, name):
    return "So you want "+name

class D(B,C):
  pass

a = A(None) # a has no proto
b = B(a) # a is the proto for b
print b.x
print b.getY() # this will not work in most implementations

d = D(a) # a is the proto for d
print d.x
print d.getY()
print d.z

Вот это Суть

1 голос
/ 25 ноября 2012

Я знаю, что это довольно старо, но я хотел бы добавить свои $ .02:

https://gist.github.com/4142456

Единственная проблемная вещь здесь - это добавление методов. У JavaScript гораздо более элегантный синтаксис с функциональными литералами, и это действительно трудно обойти. Лямбда не совсем порезалась. Поэтому я решил добавить неудобный method метод для добавления методов.

Doctests включены в суть, и все это работает.

EDIT:

Обновлено gist, чтобы больше не использовать method метод экземпляра. Однако нам все еще нужно определить функцию заранее.

В любом случае, наиболее важная часть прототипной объектной модели реализована в этой сущности. Другие вещи - это просто синтаксический сахар (было бы неплохо иметь их, но это не значит, что объектную модель-прототип нельзя использовать).

1 голос
/ 07 января 2011

Короткая версия, да, но это немного сложнее, чем JS.

С Программирование метаклассов на Python :

>>> class ChattyType(type):
...     def __new__(cls, name, bases, dct):
...         print "Allocating memory for class", name
...         return type.__new__(cls, name, bases, dct)
...     def __init__(cls, name, bases, dct):
...         print "Init'ing (configuring) class", name
...         super(ChattyType, cls).__init__(name, bases, dct)
...
>>> X = ChattyType('X',(),{'foo':lambda self:'foo'})
Allocating memory for class X
Init'ing (configuring) class X
>>> X, X().foo()
(<class '__main__.X'>, 'foo')

Также проверьте Что такое метакласс в Python .

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

...