2012-05-03 3 views
0

Класс может действовать как строка через свой метод __str__ или как функцию через свой метод __call__. Может ли он действовать, как, скажем, список или кортеж?Python: может ли экземпляр класса оценить любой тип?

class A (object): 
    def __???__ (self): 
     return (1, 2, 3) 

>>> a = A() 
>>> a * 3 
(1, 2, 3, 1, 2, 3, 1, 2, 3) 

EDIT ...

Вот лучший пример, чтобы помочь прояснить выше.

class Vector (object): 
    def __init__ (self): 
     self.vec = (1,2,3) 
    def __???__ (self): 
     # something like __repr__; non-string 
     return self.vec 

class Widget (object): 
    def __init__ (self): 
     self.vector = Vector() 

>>> w = Widget() 
>>> w.vector 
(1, 2, 3) # not a string representation (at least, before being repr'd here) 

В принципе, я хочу что-то вроде __repr__, которая не возвращает строку, а возвращает кортеж (или список), когда я просто ссылаться на имя, указывающее на экземпляр вектора, но я не хочу терять остальные возможности в экземпляре, такие как доступ к другим свойствам и методам. Я также не хочу использовать w.vector.vec, чтобы получить данные. Я хочу, чтобы вектор работал как атрибут кортежа w, но все же мог делать что-то вроде w.vector.whatever() или переопределять __mul__, чтобы я мог масштабировать вектор через w.vector * 5. Возможное?

+1

Класс не действует как строка при вызове '__str__', который он создает, и возвращает новый строковый объект. Я действительно не вижу, что вы получаете ... –

+2

Подкласс 'tuple'. –

ответ

3

В зависимости от того, что ваша цель, вы можете создать класс, который наследуется от встроенных классов как list или tuple:

>>> class A(tuple): 
...  def speak(self): 
...   print "Bark!" 
... 
>>> a = A((1,2,3)) # extra parens needed to distinguish single tuple arg from 3 scalar args 
>>> a * 3 
(1, 2, 3, 1, 2, 3, 1, 2, 3) 
>>> a.speak() 
Bark! 

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

import math 

class Vector(tuple): 
    def magnitude(self): 
     return math.sqrt(self[0]*self[0]+self[1]*self[1]+self[2]*self[2]) 
+0

У меня будет тест, но я думаю, что это ответ. Я в основном хочу кортеж, что я могу делать другие вещи с помощью /, например, добавлять свойства и методы и получать к ним доступ, но если я просто использую * его * где угодно, я хочу, чтобы он был «кортежем». –

+0

Какая у вас больше цель? Вам может понадобиться Panda3D или что-то в этом роде. –

+0

Это смешно. Мы фактически используем Panda в моей компании. Моя цель - иметь что-то вроде векторов как константные свойства определенных экземпляров класса, к которым можно легко получить доступ как свойства экземпляра (iewvector), просто масштабированные (например, 'w.vector * 3' масштабирует каждый компонент) , и манипулировать многими другими способами с помощью пользовательских методов (egwvector.inWorld()). Я также хочу, чтобы эти свойства могли обслуживать дополнительную информацию о себе (например, w.vector.property). –

0

Класс не действует как строка при вызове str. Он создает и возвращает NEW-строковый объект. В основном, когда вы звоните str(something) на объекте, это то, что происходит на самом деле:

a = str(someObject) 

a = someObject.__str__() 

Таким образом, функция str в принципе можно рассматривать как сделать это:

def str(variable): 
    return variable.__str__() 

То же самое верно, когда вы звоните list() , tuple(), set() и так далее. Если то, что я думаю, что вы спрашиваете, правильно:

tuple(), list() и set() все называют __iter__() метод класса, так что вы хотите сделать это:

class MyClass(object): 
    ... 
    def __iter__(self): 
     ... 
     return myIterable 
+0

Я понимаю, что он создает и возвращает строку, но для меня это соответствует определению класса, действующего как строка. Я не знаю, как это будет работать иначе. \ _ \ _ iter \ _ \ _ является самым близким, к которому я пришел до сих пор, но он по-прежнему требует дополнительной работы, например, для вызова iter. Возможно, у меня нет проблемы, которая достаточно хорошо определена у меня в голове. –

+1

@GaryFixler: Вам не нужно называть '__iter __()'. Использование экземпляра 'A' ​​в любом месте, где требуется итерируемый тип, будет вызывать' __iter __() 'неявно. Например, цикл 'для элемента в A:' является синтаксическим сахаром для 'для элемента в A .__ iter __():'. Поэтому, если вы определяете '__iter __()', этого достаточно, чтобы иметь возможность запускать экземпляр 'A' в списки или использовать их как списки (хотя, если вы хотите реализовать произвольный доступ, вам придется реализовать' __setitem __() ' и '__getitem __()' тоже.) –

+0

@GaryFixler: Это не требует дополнительной работы. 'list (myObject)' неявно вызывает myObject .__ iter __(), как и любое выражение, требующее итератора. –

2

Для конкретного поведение в вашем примере (A*3 дает три конкатенированные копии данных в A), которые вы хотите реализовать оператором __mul__().

Например, они эквивалентны:

>>> a = [1,2,3] 
>>> a*3 
[1, 2, 3, 1, 2, 3, 1, 2, 3] 
>>> a.__mul__(3) 
[1, 2, 3, 1, 2, 3, 1, 2, 3] 
>>> 

Вообще, если вы хотите реализовать тип последовательности вы должны реализовать все operations defined for sequence types. Вы должны определить -

  • каких A[3] средства (__getitem__(), __setitem__())
  • каких A[1:10] средства (__getslice__())
  • каких for item in A: средства (__iter__())

и так далее.

Вот полный список методов, определенных на list с:

>>> pprint.pprint(dict(list.__dict__)) 
{'__add__': <slot wrapper '__add__' of 'list' objects>, 
'__contains__': <slot wrapper '__contains__' of 'list' objects>, 
'__delitem__': <slot wrapper '__delitem__' of 'list' objects>, 
'__delslice__': <slot wrapper '__delslice__' of 'list' objects>, 
'__doc__': "list() -> new empty list\nlist(iterable) -> new list initialized from iterable's items", 
'__eq__': <slot wrapper '__eq__' of 'list' objects>, 
'__ge__': <slot wrapper '__ge__' of 'list' objects>, 
'__getattribute__': <slot wrapper '__getattribute__' of 'list' objects>, 
'__getitem__': <method '__getitem__' of 'list' objects>, 
'__getslice__': <slot wrapper '__getslice__' of 'list' objects>, 
'__gt__': <slot wrapper '__gt__' of 'list' objects>, 
'__hash__': None, 
'__iadd__': <slot wrapper '__iadd__' of 'list' objects>, 
'__imul__': <slot wrapper '__imul__' of 'list' objects>, 
'__init__': <slot wrapper '__init__' of 'list' objects>, 
'__iter__': <slot wrapper '__iter__' of 'list' objects>, 
'__le__': <slot wrapper '__le__' of 'list' objects>, 
'__len__': <slot wrapper '__len__' of 'list' objects>, 
'__lt__': <slot wrapper '__lt__' of 'list' objects>, 
'__mul__': <slot wrapper '__mul__' of 'list' objects>, 
'__ne__': <slot wrapper '__ne__' of 'list' objects>, 
'__new__': <built-in method __new__ of type object at 0x1E1DACA8>, 
'__repr__': <slot wrapper '__repr__' of 'list' objects>, 
'__reversed__': <method '__reversed__' of 'list' objects>, 
'__rmul__': <slot wrapper '__rmul__' of 'list' objects>, 
'__setitem__': <slot wrapper '__setitem__' of 'list' objects>, 
'__setslice__': <slot wrapper '__setslice__' of 'list' objects>, 
'__sizeof__': <method '__sizeof__' of 'list' objects>, 
'append': <method 'append' of 'list' objects>, 
'count': <method 'count' of 'list' objects>, 
'extend': <method 'extend' of 'list' objects>, 
'index': <method 'index' of 'list' objects>, 
'insert': <method 'insert' of 'list' objects>, 
'pop': <method 'pop' of 'list' objects>, 
'remove': <method 'remove' of 'list' objects>, 
'reverse': <method 'reverse' of 'list' objects>, 
'sort': <method 'sort' of 'list' objects>} 
+0

Я знаю об операторе \ _ \ _ mul \ _ \ _ и будет реализовывать его для этой потребности. Однако в моем примере я просто пытался показать, что он действительно возвращает кортеж, а не строковое представление кортежа. Это простое использование класса, чтобы действовать как тип, сохраняя при этом другие возможности, такие как вызывающие методы, которые меня интересуют. Может быть, я могу как-то подклассифицировать кортеж? –

+1

@GaryFixler: Если вы хотите, чтобы объект был привязан к кортежу, вам нужно сделать объект «итерабельным». (помните, что объект 'TypeError: 'A' не является итерируемой ошибкой? Это то, о чем он говорит.) Для выполнения этого нужно быть достаточно __iter __()'. Тогда 'tuple (A)', который расширяется до 'tuple (A .__ iter __())', должен делать то, что вы хотите. Точно так же будут выполняться петли над «А», будут выполняться перечни понятий и т. Д. –

+1

@ Li-aungYip обратите внимание, что вам не нужно реализовывать '__setitem__' как часть протокола последовательности (если вы явно не хотите, чтобы он был изменчивым, в этом случае вы хотите реализовать' __delitem__', 'insert' и другие как), а '__ {get, set, del} slice__' некоторое время устарел в пользу передачи объекта' slice' '__ {get, set, del} item__'. Лучший и простой способ для класса реализовать полный протокол последовательности - наследовать от 'collections.Sequence' (или' collections.abc.Sequence' от 3.3). (если, конечно, имеет смысл наследовать от существующего типа последовательности). – lvc

Смежные вопросы