1

In Python's pathlib module you can create a Path object from another Path and they will be equivalent:

p = Path('some/path')
p2 = Path(p)
p == p2
# True

I have a File class that I would like to implement this same behavior, where if I pass a File object to the File constructor, it will simply return the original object that was passed in. What is the correct way to do so? Do I need to override the __new__ method to achieve this?

3
  • 4
    "if I pass a File object to the File constructor, it will simply return the original object that was passed in." that's actually not what is happening above, you'll note, p is p2 will evaluate to False. they are equal but not identical. Although, one example of where you will see this behavior is with strings, i.e. str(some_str) is some_str happens to be true in CPython. Commented Oct 29, 2021 at 3:41
  • 3
    You could extract the data and create a new instance if it's simple. Or you could check the type and make a deep copy. Commented Oct 29, 2021 at 3:55
  • @d.b thanks! That's very helpful. I followed this solution. Commented Oct 31, 2021 at 1:43

2 Answers 2

1

If you really want the constructor to return the original object then you have to override __new__ method:

def __new__(cls, *args):
    if isinstance(args[0], File):
        return args[0]
    instance = super().__new__(cls, *args)
    # do initialization
    return instance

Note that you cannot use __init__ because it would be called even if no new instance is created.

A cleaner solution is to use a function which return either the instance or a new instance:

def File(*args):
    if isinstance (args[0], File_):
        return args[0]
    return File_(*args)

where File_ is the original class

Sign up to request clarification or add additional context in comments.

Comments

1

As @d.b mentions in the comments, you can "extract the data and create a new instance." This happens to be exactly how Path does it, using @classmethods. Here is the key function from the source (simplified here for clarity), which is called from __new__:

@classmethod
def _parse_args(cls, args):
    parts = []
    for a in args:
        if isinstance(a, PurePath): # <--- Here
            parts += a._parts       # <---
        else:
            a = os.fspath(a)
            if isinstance(a, str):
                parts.append(str(a))
            else:
                raise TypeError()
    return cls._flavour.parse_parts(parts)

Then p == p2 returns True because of the class's definition of __eq__, though p2 is not the original object.

1 Comment

Very helpful, thanks! I ended up implementing a solution extracting the data and creating a new instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.