Как создать подкласс int и использовать isinstance для идентификации его экземпляров?

1

Я хочу подкласс int (или какой-либо другой аналогичный встроенный численный тип), который я могу явно ввести проверку.

Этот q & a похож, но не ответил на то, что я точно вижу: Sub-class для встроенного типа Python, такого как int

Вот пример, который я пытаюсь достичь:

class MyId( int ) : pass 

class MyCollection() :

    def __init__( self ):
        self.__id = MyId( 0 )

    def nextId( self ) :        
        self.__id+=1
        print "isinstance",  isinstance(self.__id, MyId)
        return self.__id

К сожалению, мой призыв isinstance возвращает False. Как мне добиться успеха (в идеале, с той же базовой концепцией)? Очевидно, что добиться этого - это дать MyId классу "есть", а не "является" отношением к int... но я подумал, что было бы лучше сделать его int с определенным "именем".

В настоящее время я пишу это в Py2, но любые ответы на кросс-версию оцениваются, если это применимо.

  • 3
    Если вы не хотите реализовывать все соответствующие магические методы ( __add__, __radd__, etc. ) В MyId , вы можете просто использовать self.__id = MyId(self.__id + 1) .
  • 2
    Если вашей целью является проверка типа, почему бы просто не проверить, что __id является int ? isinstance(self.__id, int)
Показать ещё 5 комментариев
Теги:
python-2.x
isinstance
subclass

4 ответа

3

Это потому, что вам нужно переопределить метод __add__.

Если вы не переопределите этот метод, он будет использовать встроенный метод int __add__ который возвращает новый целочисленный объект.

См. Эту тему, которая объясняет это поведение, как упоминалось в комментарии @martineau.

class MyId( int ):
    def __add__(self, i):
        return MyId(super(MyId, self).__add__(i))

class MyCollection() :

    def __init__( self ):
        self.__id = MyId( 0 )

    def nextId( self ) :        
        self.__id += 1
        print "isinstance",  isinstance(self.__id, MyId)
        return self.__id

a = MyCollection()
a.nextId()

Печать: isinstance True

  • 0
    Было бы лучше ответить, если бы вы объяснили, почему __add__() необходимо реализовать.
  • 0
    Хм. Это, безусловно, правильный ответ, я проголосовал за него. Но я не влюблен в это. Я ненавижу идею, что мне нужно переопределить добавить функции, которые я пытаюсь получить бесплатно. В этом смысл подкласса. Ответ @schwobaseggl (опубликованный в виде комментария) может быть лучше ... Но я думаю, что он тоже не идеален ...
Показать ещё 12 комментариев
0

По предложению Дюны я просто полностью отказался от всего понятия int. Как он отметил, любой ванильный объект может неявно использоваться как уникальный ключ!

На самом деле MyId можно определить как просто: class MyId: pass. Часто это было бы - совершенно полезный, неявно уникальный ключ!

Однако для моего варианта использования мне нужно передавать эти ключи взад и вперед по вспомогательным процессам (через очереди multiprocessing). Я столкнулся с проблемой этого подхода с ультралегким весом, поскольку значение хэша изменилось бы, когда объекты, где мариновали и проталкивали процессы. Еще одна второстепенная проблема заключалась в том, что я хотел сделать эти объекты легкими для регистрации и ручного чтения/сопоставления журналов. Таким образом, я пошел с этим:

class _MyIdPrivate: pass
class MyId :
    def __init__( self ):
        self.__priv = _MyIdPrivate() 
        self.__i = hash( self.__priv )            
    def __str__( self ): return str( self.__i )
    def __hash__( self ): return self.__i
    def __eq__( self, other ): 
        try: return self.__i == other.__i
        except: return False     

class MyCollection :

    def __init__( self ):
        self.__objs={}

    def uniqueId( self ): return MyId()

    def push( self, i, obj ):
        self.__objs[ i ] = obj     

    def pop( self, i ):
        return self.__objs.pop( i, None )     

c = MyCollection()
uId = c.uniqueId()
print "uId", uId
print "isinstance", isinstance(uId, MyId)
c.push( uId, "A" )
print c.pop( MyId() )
print c.pop( uId )

Как вы можете видеть, я включил короткий и сладкий подход в более всеобъемлющий/подробный. Когда я создаю объект MyId, я создаю член _MyIdPrivate и беру хэш этого момента в момент создания. При травлении и нажатии на подпроекты этот хэш файл _MyIdPrivate изменится - но это не имеет значения, потому что я захватил начальное значение, и все заканчивается от этого.

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

Как предположил Дюны, я мог бы также использовать uuid. Я вижу плюсы и минусы этого против этого...

  • 0
    Объединение __hash__ и вашего метода increment - очень плохая идея. Объекты не должны иметь изменяемые значения хеш-функции или, по крайней мере, не должны быть видоизменены, пока они являются ключами в отображении. Если вам нужно изменить хеш-значение ключа, вы больше не сможете ссылаться на него. См. Docs.python.org/3/reference/datamodel.html#object.__hash__ для получения дополнительной информации.
  • 0
    Хороший вопрос, @Dunes! Я вижу логический недостаток в том, что у меня здесь есть. Можете ли вы предложить конкретную переписать для того, что я пытаюсь достичь?
Показать ещё 10 комментариев
0

Похоже, что вы после этого можете проверить, что передаваемые значения были созданы определенным образом. В Python 3.5. 2+ имеется модуль typing который предоставляет NewType. Это позволяет делать статический анализ вашего кода, чтобы убедиться, что он делает то, что вы ожидаете от него. Пример, приведенный в документации:

from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)

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

def get_user_name(user_id: UserId) -> str:
    ...

# typechecks
user_a = get_user_name(UserId(42351))

# does not typecheck; an int is not a UserId
user_b = get_user_name(-1)

NewType время выполнения фактическая проверка типов не выполняется, и значение, возвращаемое NewType является просто сквозной функцией, которая возвращает свой аргумент без изменений. Это также означает, что вы не можете делать такие вещи, как isinstance(obj, UserId), поскольку UserId не является фактическим классом. Что значит, так это, как указано в документации, статические проверки типов помогут выявить логические ошибки, которые кажутся вам такими.

  • 0
    Мне нравится эта идея, но она не создает отдельный тип. type(UserId(1)) == int равно true. Сам UserId является функцией, а не типом.
0

Вместо подкласса int просто проверьте, что ваша переменная экземпляра является int.

class MyCollection():

    def __init__( self ):
        self.__id = 0

    def nextId( self ) :        
        self.__id += 1
        print "isinstance",  isinstance(self.__id, int)
        return self.__id
  • 0
    Спасибо, но это не отвечает моим потребностям. Задача состоит в том, чтобы определить, возвращаются ли некоторые полиморфные функции и / или имеют дело с конкретным типом объекта, потому что от этого зависит контекст того, что делать с такими объектами. Мне нужно различать int и этот «тип идентификатора»
  • 0
    Тогда я думаю, что отношения «есть» - это ваше лучшее. Похоже, вы не хотите, чтобы __id рассматривался как int. Лучший способ предотвратить это - создать новый класс.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню