Reputation: 724
class FileHandler:
def __new__(cls, path, *more_args, **kwargs):
path = do_stuff_to_path(path)
new_arg = do_more_stuff(path)
if check_format(path) == "video":
# return VideoHandler(path, new_arg, *more_args, **kwargs)
elif check_format(path) == "image":
# return ImageHandler(path, new_arg, *more_args, **kwargs)
else:
# just return FileHandler(path, new_arg, *more_args, **kwargs)
def __init__(path, new_arg, arg1, arg2, kwarg1=None):
# stuff
class VideoHandler(FileHandler):
# stuff
class ImageHandler(FileHandler):
# stuff
FileHandler("path_to_video") # instance of VideoHandler
FileHandler("path_to_image") # instance of ImageHandler
How do I make A return the right subclass while at the same time preserving the changes I made to the arguments, or passing new arguments? Every existing question about this was either written a decade ago for python 2 or suggests using a factory function, which is not what I want.
Upvotes: 1
Views: 619
Reputation: 43
Even if the question has been marked as answered, here is a way to do nearly what you are looking for. Except getting a FileHandler instance if no case have been matched.
Here is a basic example for anyone interested, where I didn't use the names defined in the question, because I think that it's now useless.
class Foo :
instantiation = False
def __new__(cls, *args, mode=None, **kwargs) :
if Foo.instantiation :
Foo.instantiation = False
return super(Foo, cls).__new__(cls)
Foo.instantiation = True
match mode :
case '1' :
return Bar1.__new__(Foo, *args, **kwargs)
case '2' :
return Bar2.__new__(Foo, *args, **kwargs)
def __init__(self) -> None:
pass #do some stuff
############
class Bar1 (Foo) :
def __new__ (cls, value) :
return Foo.__new__(Bar1, value)
def __init__(self, value, mode) -> None:
Foo.__init__(self)
self.value = value
class Bar2 (Foo) :
def __new__ (cls, value) :
return Foo.__new__(Bar2, value)
def __init__(self, value, mode) -> None:
Foo.__init__(self)
self.value = value
Note that in the code above mode
is necessarily a keyword argument.
However some modifications are possible to make it both keyword and positional (it will depends on the context where it's used).
So now the pros and cons (mostly the cons) of this implementation.
Pros :
Bar1
and Bar2
truly derivates from Foo
.Cons :
mode
in Bar1.__init__()
and Bar2.__init__()
even if it's not usedTo conclude, it's strange but not impossible.
Upvotes: 1
Reputation: 531075
Rather than forcing __new__
to do this, I would define a separate class method for deciding what to do based on the given path
:
class FileHandler:
def __init__(self, path, new_arg, arg1, arg2, kwarg1=None):
# stuff
@classmethod
def from_path(cls, path, *args, **kwargs):
path = do_stuff_to_path(path)
new_arg = do_more_stuff(path)
if check_format(path) == "video":
cls = VideoHandler
elif check_format(path) == "image":
cls = ImageHandler
return cls(path, new_arg, *args, **kwargs)
fh1 = FileHandler.from_path("path_to_video") # instance of VideoHandler
fh2 = FileHandler.from_path("path_to_image") # instance of ImageHandler
Now when you actually instantiate the object, only the modified arguments are passed to __init__
, since you never actually call the selected type with the original arguments.
Upvotes: 2