Как создать подкласс int и использовать isinstance для идентификации его экземпляров? - PullRequest
0 голосов
/ 30 ноября 2018

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

Эти вопросы и ответы похожи, но они не отвечают на то, что я вижу точно: Подклассирование встроенного типа 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, но любые перекрестные версии приветствуются, если это применимо.

Ответы [ 4 ]

0 голосов
/ 30 ноября 2018

Согласно предложению Дюны, я просто полностью отбросил концепцию 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 голосов
/ 30 ноября 2018

Вместо создания подкласса 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 голосов
/ 30 ноября 2018

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

from typing import NewType

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

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

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, является просто сквозной функцией, которая возвращает свой аргумент без изменений,Это также означает, что вы не можете делать такие вещи, как isinstance(obj, UserId), поскольку UserId не является реальным классом.Что означает, что, как указано в документации, средства проверки статического типа помогут обнаружить логические ошибки - что похоже на то, что вам нужно.

0 голосов
/ 30 ноября 2018

Это потому, что вам нужно переопределить метод __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

...