0

As a part of memoizing mechanism, I have a generator, I tee it, and I need to check its type with isinstnace.

>>> gen = (i for i in range(5))
>>> one, two = itertools.tee(gen, 2)

then

>>> type(one), type(two)
(itertools.tee, itertools.tee)

as expected, but

>>> isinstance(one, itertools.tee)
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

so what is the instance of tee?

meto
  • 3,425
  • 10
  • 37
  • 49

5 Answers5

4

itertools.tee is a function, not a class (see type(itertools.tee)). It returns objects that are (somewhat confusingly) instances of a tee class, but that class is not available in the itertools namespace. I'd just grab it from an instance using type(). If you don't have an instance handy, create one. This seems to be the minimal amount of code to get the tee type:

TeeType = type(itertools.tee(())[0])

It does appear that tee consistently uses the same class for each invocation.

kindall
  • 178,883
  • 35
  • 278
  • 309
4

tee is a function that returns an internal implementation type used by Python. In general, it shouldn't matter that it's a tee or any other iterator/generator type.

That said, if you really need to type test like this, you can do so, you just need to derive the "true" type of a tee instance the same way you checked the type already, by using type. For example, to store the type of tee instances in a variable named Tee, you'd do:

Tee = type(itertools.tee(())[0])

which calls tee to get instances, then uses type to get their class.

This is the only portable way to do it for the record; the actual types involve (being implementation internal) change from release to release; in Python 3.5, you have access to the raw type as itertools._tee, but obviously in your version, it's not exposed like that.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
2

isinstance is used to check whether something says it's a duck, regardless of whether or not it is a duck.

To answer your immediate question, isinstance.tee is a method, not a class, or type or tuple of types:

>>> type(itertools.tee)
<class 'builtin_function_or_method'>

(In fact, on my version of python, class has altogether been removed from th list of options). Python is a 'duck typed' language. Basically, in order for 'isinstance' to say that something is an instance of another thing, it must inherit from it in some way.

Example:

>>> type(str)
<class 'type'>
>>> s = "foo"
>>> isinstance(s,str)
True
>>> class myString(str): #myString inherits from str (a type)
...   def __init__(self,s):
...     super().__init__()
... 
>>> s = myString("foo")
>>> s
'foo'
>>> isinstance(s,str) 
True

Primarily, these base classes that are inherited should be (wherever possible) base types or abstract base classes. Why is this? Well, Python's developers want to avoid a situation where we have a million new user-types like FooString and BarInt that people start checking for instance of. This should be avoided.

The ABC's provide a way of allowing an object to say it is something, like an Integral, or a String, without (necessarily) having to be a string.

But, Python's duck typing is built on implementing protocols. Protocols are defined by one or more "special" (or magic or dunder) methods in the class. Determining what special methods are part of a class can be done with dir(myObj):

>>> dir(itertools.tee)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']

Or, even better, where possible, just try to do something with it, and if it fails, you know it's not a duck:

try: duck(something)
except: print("Not a duck!")
Keozon
  • 998
  • 10
  • 25
2

Let's say we define a class MyClass:

class MyClass(object):
    pass

We then define a function that overrides that class:

def MyClass(x=MyClass):
    return x()

If we call that function, we will get an instance of the original MyClass:

>>> instance = MyClass()
>>> type(instance)
<class '__main__.MyClass'>

It looks like MyClass is a class because the type of its return value has the same name, but take a look:

>>> type(MyClass)
<type 'function'>

MyClass is a function that returns an instance of MyClass, but they are different. It is the same thing with tee. tee is a function that returns an instance of tee. When you see if that instance is of type tee, you aren't actually checking a class or a type; you are checking a function. isinstance() will not allow that, so hence the error. The way to get the class would be to create an instance:

>>> tee = type(itertools.tee(())[0])
>>> isinstance(one, tee)
True
zondo
  • 19,901
  • 8
  • 44
  • 83
-1

tee is a function that returns an instance that isn't itertools.tee. One way to check that is (somewhat hacky) to query the class name of the instance:

>> one.__class__.__name__ == "tee"
True
mfessenden
  • 598
  • 4
  • 12
  • 1
    As I noted on another answer, this is incredibly brittle; the internal name of the type changes from release to release (it's an implementation detail, not part of the spec, and can change at any time); in Python 3.5, the type name is `_tee` for instance. – ShadowRanger Mar 09 '16 at 01:40