Jayendra Parmar
Jayendra Parmar

Reputation: 774

How to set builtin class methods with python type function?

The following code works as expected

class Foo: pass
a1 = Foo
a2 = Foo
print(a1 == a2)
# True

now if I dynamically generate them and compare it it doesn't work

def eq(a,b):
  return str(a) == str(b)

t1 = type("Foo", (), {"__eq__": eq})
t2 = type("Foo", (), {"__eq__": eq})

print(t1 == t2)
# False

Moreover when running this, I get the error

print(t1.__eq__(t2))

Traceback (most recent call last):
  File "foo.py", line 51, in <module>
    print(t1.__eq__(t2))
TypeError: eq() missing 1 required positional argument: 'b'

What's the correct way to setup a builtin methods with using a type function ?

sidenote:
I need this kind of functionality, where I am dynamically creating the types either from types(in this case it's a identity function where I return output same as input) or type annotation which are not of type type but either typing.Alias or typing.Generic. Here is the snippet to get bigger picture

from typing import List
class Foo: pass

def build_type(type_):
  '''Build type definition of new user-defined types or annotations.'''

  if isinstance(type_, type):
    return type_
  else:
    origin = type_.__origin__ if hasattr(type_, "__origin__") else None
    args = type_.__args__ if hasattr(type_, "__args__") else None
    type_name = str(type_)
    attrs = {
      "__origin__": origin,
      "__args__": args,
      "__hash__": hash((type_name, origin, args))
    }
    return type(type_name, (), attrs)

t1 = build_type(Foo)
t2 = build_type(Foo)
print(t1 == t2) # True

a1 = build_type(List[Foo])
a2 = build_type(List[Foo])

print(a1 == a2) # False

Upvotes: 0

Views: 61

Answers (2)

Serge Ballesta
Serge Ballesta

Reputation: 149025

As you were told in comments, methods have to be members of the class, not of the instance.

As you are dynamically building classes and not simple objects, you should use a custom meta-class with a specific __eq__ method:

You example would become:

...
class Builder(type):
"""Custom meta-class defining type equality as name equality"""
    def __eq__(self, other):
        return str(self) == str(other)

def build_type(type_):
  '''Build type definition of new user-defined types or annotations.'''

  if isinstance(type_, type):
    return type_
  else:
    origin = type_.__origin__ if hasattr(type_, "__origin__") else None
    args = type_.__args__ if hasattr(type_, "__args__") else None
    type_name = str(type_)
    attrs = {
      "__origin__": origin,
      "__args__": args,
      "__hash__": hash((type_name, origin, args))
    }
    return Builder(type_name, (), attrs)
...

If you run it, you should get as expected:

True
True

Upvotes: 1

Dariusz Krynicki
Dariusz Krynicki

Reputation: 2718

your code currently returns:

id(a1)
140415204390672

id(a2)
140415204390672

# a1 and a2 have same object IDs

id(t1)
140415116432032

id(t2)
140415116465120

# t1 and t2 have different object ID

but I guess you want to compare:

a1.__name__ == a2.__name__
True

where a1.__name__ is 'Foo'

Upvotes: 0

Related Questions