Я хочу подкласс 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, но любые ответы на кросс-версию оцениваются, если это применимо.
Это потому, что вам нужно переопределить метод __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
__add__()
необходимо реализовать.
По предложению Дюны я просто полностью отказался от всего понятия 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. Я вижу плюсы и минусы этого против этого...
__hash__
и вашего метода increment
- очень плохая идея. Объекты не должны иметь изменяемые значения хеш-функции или, по крайней мере, не должны быть видоизменены, пока они являются ключами в отображении. Если вам нужно изменить хеш-значение ключа, вы больше не сможете ссылаться на него. См. Docs.python.org/3/reference/datamodel.html#object.__hash__ для получения дополнительной информации.
Похоже, что вы после этого можете проверить, что передаваемые значения были созданы определенным образом. В 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
не является фактическим классом. Что значит, так это, как указано в документации, статические проверки типов помогут выявить логические ошибки, которые кажутся вам такими.
type(UserId(1)) == int
равно true. Сам UserId
является функцией, а не типом.
Вместо подкласса 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
__id
рассматривался как int. Лучший способ предотвратить это - создать новый класс.
__add__, __radd__, etc.
) ВMyId
, вы можете просто использоватьself.__id = MyId(self.__id + 1)
.__id
являетсяint
?isinstance(self.__id, int)