Это плохая форма переопределить оператор "точка" в Python? - PullRequest
0 голосов
/ 25 августа 2018

Обычно точка в Python обозначает членство в классе:

class A:
    a = 1

>>> A.a
1

Иногда язык кажется недостаточно гибким, чтобы полностью выразить идею из областей вне компьютерной науки.Рассмотрим следующий пример, который (довольно кратко) использует один и тот же оператор, чтобы казаться чем-то совершенно другим.

class Vector:
    def __init__(self, data):
        self.data = list(data)

    def dot(self, x):
        return sum([a*b for a, b in zip(self.data, x.data)])

    def __getattr__(self, x):
        if x == 'Vector':
            return lambda p: self.dot(Vector(p))
        return self.dot(globals()[x])

Здесь мы взяли на себя __getattr__(), так что во многих сценариях, где Python пытаетсянайти атрибут из нашего вектора, вместо этого он вычисляет математическое произведение точек.

>>> v = Vector([1, 2])
>>> v.Vector([3, 4])
11

>>> v.v
5

Если такое поведение ограничено областью интересов, Что-то не так с таким шаблоном проектирования?

Ответы [ 3 ]

0 голосов
/ 25 августа 2018

Если вы хотите использовать векторы, есть специальное имя метода, которое не упоминается в большей части и называется __matmul__. Это происходит с соответствующими им на месте и отраженными методами __imatmul__ и __rmatmul__. Оператор @:

a @ b
# corresponds to
a.__matmul__(b)
0 голосов
/ 25 августа 2018

Это плохая идея.

Почему? Потому что «оператор точки», как вы его называете, с самого начала не является оператором. Это потому, что «операнд» в правой части интерпретируется как строка, а не как выражение. Это может показаться вам незначительным, но оно имеет множество проблемных последствий:

  • Программисты Python используются для foo.bar, что означает «Возьмите атрибут bar объекта foo». Превращение точки в оператора точечного продукта разрушает это ожидание и вводит в заблуждение людей, которые читают ваш код. Это не интуитивно понятно.

  • Это неоднозначно, потому что вы не можете знать, пытается ли пользователь вычислить точечное произведение или получить доступ к атрибуту. Рассмотрим:

    >>> data = Vector([1, 2])
    >>> v.data  # dot product or accessing the data attribute?
    

    Имейте в виду, что методы также являются атрибутами:

    >>> dot = Vector([1, 2])
    >>> v.dot  # dot product or accessing the dot method?
    
  • Поскольку правый операнд интерпретируется как строка, вы должны перепрыгнуть через целую кучу обручей, чтобы превратить эту строку во что-то полезное - как вы пытались сделать с globals()[x], который выглядит до переменной в глобальной области видимости. Проблема в том, что - в определенных ситуациях - совершенно невозможно получить доступ к переменной только по ее имени. Независимо от того, что вы делаете, вы никогда не сможете получить доступ к переменной, которая больше не существует, потому что она уже была собрана сборщиком мусора:

    def func():
        v2 = Vector([1, 2])
    
        def closure_func():
            return v.v2  # this will never work because v2 is already dead!
    
        return closure_func
    
    closure_func = func()
    result = closure_func()
    
  • Поскольку правый операнд является строкой, вы не можете использовать произвольные выражения в правой части. Вы ограничены переменными; попытка использовать что-либо еще с правой стороны вызовет какое-то исключение. И что еще хуже, он даже не выдаст соответствующие TypeError, как это сделали бы другие операторы:

    >>> [] + 1
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: can only concatenate list (not "int") to list
    >>> v.1
      File "<stdin>", line 1
        v.1
          ^
    SyntaxError: invalid syntax
    
  • В отличие от реальных операторов, «оператор точки» может быть реализован только в левом операнде. Все остальные операторы могут быть реализованы в одном из двух соответствующих дундерметодов, например, __add__ и __radd__ для оператора +. Пример:

    >>> class Incrementer:
    ...     def __radd__(self, other):
    ...         return other + 1
    ... 
    >>> 2 + Incrementer()
    3
    

    Это невозможно с вашим точечным продуктом:

    >>> my_v = MyCustomVector()
    >>> v.my_v
    AttributeError: 'MyCustomVector' object has no attribute 'data'
    

Итог: реализация метода dot в вашем классе Vector - это путь. Поскольку точка не является реальным оператором, попытка превратить ее в единицу неизбежна для обратного выстрела.

0 голосов
/ 25 августа 2018

Я бы не рекомендовал это.Что вы подразумеваете под "языком недостаточно гибким, чтобы выразить идею"?В вашем примере v.dot(u) является выразительным и имеет желаемый эффект.Кстати, именно так numpy делает это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...