Faking whether an object is an Instance of a Class in Python
Question:
Suppose I have a class FakePerson
which imitates all the attributes and functionality of a base class RealPerson
without extending it. In Python 3, is it possible to fake isinstance()
in order to recognise FakePerson
as a RealPerson
object by only modifying the FakePerson
class. For example:
class RealPerson():
def __init__(self, age):
self.age = age
def are_you_real(self):
return 'Yes, I can confirm I am a real person'
def do_something(self):
return 'I did something'
# Complicated functionality here
class FakePerson(): # Purposely don't extend RealPerson
def __init__(self, hostage):
self.hostage = hostage
def __getattr__(self, name):
return getattr(self.hostage, name)
def do_something(self):
return 'Ill pretend I did something'
# I don't need complicated functionality since I am only pretending to be a real person.
a = FakePerson(RealPerson(30))
print(isinstance(a, RealPerson))
The context of this is suppose I have a class that imitates most / all of the functionality of a Pandas DataFrame row (a namedtuple
object). If I have a list of rows list_of_rows
, Pandas generates a DataFrame object by pandas.DataFrame(list_of_rows)
. However, since each element in list_of_rows
is not a namedtuple
and just a ‘fake’, the constructor can’t recognise these ‘fake’ row objects as real rows even if the fake object does fake all the underlying methods and attributes of the Pandas namedtuple
.
Answers:
The isInstance() function is a python builtin who’s implementation explicitly looks for an object’s (direct, indirect or virtual) class or subclass. The ‘imitations’ you’re referring to is also known as duck typing. In your case, it looks like you do want to extend or subclass the DataFrame row. Though, you might get away with assigning the class attribute, but know that this may lead to undefined behavior as it is implementation-specific.
You may need to subclass your RealPerson
class.
class RealPerson:
def __init__(self, age):
self.age = age
def are_you_real(self):
return 'Yes, I can confirm I am a real person'
def do_something(self):
return 'I did something'
# Complicated functionality here
class FakePerson: # Purposely don't extend RealPerson
def __init__(self, hostage):
self.hostage = hostage
def __getattr__(self, name):
return getattr(self.hostage, name)
def do_something(self):
return 'Ill pretend I did something'
# I don't need complicated functionality since I am only pretending to be a real person.
class BetterFakePerson(RealPerson):
pass
BetterFakePerson.__init__ = FakePerson.__init__
BetterFakePerson.__getattr__ = FakePerson.__getattr__
BetterFakePerson.do_something = FakePerson.do_something
a = FakePerson(RealPerson(30))
print(isinstance(a, RealPerson))
b = BetterFakePerson(RealPerson(30))
print(isinstance(b, RealPerson))
Hope this answer would not be too late for you LOL
Define __class__
It seems that unittest.mock
does this, judging by the following paragraph in the docs.
If spec is an object (rather than a list of strings) then __class__
returns the class of the spec object. This allows mocks to pass isinstance()
tests.
There is bit more on that a few paragraphs onwards on the same page.
# Works, but needs ignore comment for pyre-check, if you use that
class FakePerson:
__class__ = RealPerson # pyre-ignore[15] Inconsistent override
# ...
Define __instancecheck__
? Nope!
The __instancecheck__
and __subclasscheck__
special methods are documented in the 3.3.4. Customizing instance and subclass checks section of the Datamodel chapter of the Python reference.
Reading that docs, one would think that the following is also a solution, however, it does not work. See https://stackoverflow.com/a/17736152/1047788 for more on that. One would have to implement this __instancecheck__
in RealPerson
for it to work, which is not what we want.
# Valiant attempt, but does not work
def FakePerson:
def __instancecheck__(self, instance):
return issubclass(instance, RealPerson)
# ...
Suppose I have a class FakePerson
which imitates all the attributes and functionality of a base class RealPerson
without extending it. In Python 3, is it possible to fake isinstance()
in order to recognise FakePerson
as a RealPerson
object by only modifying the FakePerson
class. For example:
class RealPerson():
def __init__(self, age):
self.age = age
def are_you_real(self):
return 'Yes, I can confirm I am a real person'
def do_something(self):
return 'I did something'
# Complicated functionality here
class FakePerson(): # Purposely don't extend RealPerson
def __init__(self, hostage):
self.hostage = hostage
def __getattr__(self, name):
return getattr(self.hostage, name)
def do_something(self):
return 'Ill pretend I did something'
# I don't need complicated functionality since I am only pretending to be a real person.
a = FakePerson(RealPerson(30))
print(isinstance(a, RealPerson))
The context of this is suppose I have a class that imitates most / all of the functionality of a Pandas DataFrame row (a namedtuple
object). If I have a list of rows list_of_rows
, Pandas generates a DataFrame object by pandas.DataFrame(list_of_rows)
. However, since each element in list_of_rows
is not a namedtuple
and just a ‘fake’, the constructor can’t recognise these ‘fake’ row objects as real rows even if the fake object does fake all the underlying methods and attributes of the Pandas namedtuple
.
The isInstance() function is a python builtin who’s implementation explicitly looks for an object’s (direct, indirect or virtual) class or subclass. The ‘imitations’ you’re referring to is also known as duck typing. In your case, it looks like you do want to extend or subclass the DataFrame row. Though, you might get away with assigning the class attribute, but know that this may lead to undefined behavior as it is implementation-specific.
You may need to subclass your RealPerson
class.
class RealPerson:
def __init__(self, age):
self.age = age
def are_you_real(self):
return 'Yes, I can confirm I am a real person'
def do_something(self):
return 'I did something'
# Complicated functionality here
class FakePerson: # Purposely don't extend RealPerson
def __init__(self, hostage):
self.hostage = hostage
def __getattr__(self, name):
return getattr(self.hostage, name)
def do_something(self):
return 'Ill pretend I did something'
# I don't need complicated functionality since I am only pretending to be a real person.
class BetterFakePerson(RealPerson):
pass
BetterFakePerson.__init__ = FakePerson.__init__
BetterFakePerson.__getattr__ = FakePerson.__getattr__
BetterFakePerson.do_something = FakePerson.do_something
a = FakePerson(RealPerson(30))
print(isinstance(a, RealPerson))
b = BetterFakePerson(RealPerson(30))
print(isinstance(b, RealPerson))
Hope this answer would not be too late for you LOL
Define __class__
It seems that unittest.mock
does this, judging by the following paragraph in the docs.
If spec is an object (rather than a list of strings) then
__class__
returns the class of the spec object. This allows mocks to passisinstance()
tests.
There is bit more on that a few paragraphs onwards on the same page.
# Works, but needs ignore comment for pyre-check, if you use that
class FakePerson:
__class__ = RealPerson # pyre-ignore[15] Inconsistent override
# ...
Define __instancecheck__
? Nope!
The __instancecheck__
and __subclasscheck__
special methods are documented in the 3.3.4. Customizing instance and subclass checks section of the Datamodel chapter of the Python reference.
Reading that docs, one would think that the following is also a solution, however, it does not work. See https://stackoverflow.com/a/17736152/1047788 for more on that. One would have to implement this __instancecheck__
in RealPerson
for it to work, which is not what we want.
# Valiant attempt, but does not work
def FakePerson:
def __instancecheck__(self, instance):
return issubclass(instance, RealPerson)
# ...