如何继承int并使用isinstance标识它的实例?

时间:2018-11-30 16:36:40

标签: python subclass python-2.x isinstance

我想继承int(或其他一些类似的内置数字类型)的类,以便可以显式地键入check。

此问与答类似,但没有完全回答我所看到的: Sub-classing a built-in Python type such as 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。如何使它成功(理想情况下,具有相同的基本概念)?显然,如何通过与int赋予MyId类“具有”而不是“是”关系来实现这一点……但我认为最好将其设为具有int的{​​{1}}特定的“名称”。

当前,我正在Py2中编写此代码,但如果适用的话,任何跨版本的答案都值得赞赏。

4 个答案:

答案 0 :(得分:3)

那是因为您需要覆盖__add__方法。

如果不重写此方法,它将使用内置的int __add__方法,该方法返回一个新的整数对象。

请参阅this topic,其中解释了@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

答案 1 :(得分: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

答案 2 :(得分:0)

这听起来像是您可以检查要传递的值是否已以特定方式创建的内容。在 Python 3.5.2 + 中,提供了NewTypetyping模块。这使您可以对代码进行静态分析,以确保它正在执行您期望执行的操作。文档中给出的示例是:

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不是实际的课程。真正的意思是,正如文档中提到的那样,静态类型检查器将帮助发现逻辑错误-看起来像您要的那样。

答案 3 :(得分:0)

根据Dun Dunes的建议,我只是完全放弃了整个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计划相比,此方法的主要优点是我得到了唯一的密钥,而无需“计算”或直接分配它。

正如Dunes的建议,我也可以使用uuid。我可以看到相对于此的利弊...