Accessing parent objects
Jugurtha Hadjar
jugurtha.hadjar at gmail.com
Sun Mar 25 11:01:57 EDT 2018
On 03/25/2018 03:25 PM, Terry Reedy wrote:
> On 3/25/2018 7:42 AM, Jugurtha Hadjar wrote:
>
>> class C2(object):
>> def __init__(self, parent=None):
>> self.parent = parent
>
> Since parent is required, it should not be optional.
>
You can still call it the way you'd call it if it were a positional
argument.
i.e:
def foo(keyword_argument=None):
print(keyword_argument)
foo(3)
foo(keyword_argument=3)
Just because it's a keyword argument doesn't mean it's not required. I
could have provided a default using `self.parent = parent or C2`, but I
didn't want to assume C2 was defined in that namespace and wanted to
give as generic a code as I could.
Furthermore, the only case I'd use a positional argument is if I were
100% certain the code will not change, which I'm not. Plus when the API
will change (and it will), it will be harder to deal with it that if it
were a keyword argument. If you change the order you'd have to change
the whole codebase where the class/function is instantiated/called,
whereas if you use a keyword argument, you could just catch the old way
call, issue a deprecation warning, then route the call for the new way call.
What benefit does the positional argument provide?
>> def foo(self):
>> print("I am {self.__class__.__name__} foo".format(self=self))
>> self.parent.foo()
>
> None.foo will raise AttributeError.
>
Right.. As I said, I tried to assume as little as possible about OP's
code and namespace. Didn't want to include C1 in __init__ signature
because I wasn't sure it was available in the namespace.
It is easy to address, though:
Example A:
class C2(object):
def __init__(self, parent=C2):
self.parent = parent
Example B:
class C2(object):
def __init__(self, parent=None):
self.parent = parent or C2
Furthermore, having a positional argument will not save us. We can still
break the code if we do the following:
class C2(object):
def __init__(self, parent):
self.parent = parent
def foo(self):
self.parent.foo()
c1 = C1()
c2 = C2(None)
c2.foo()
Making it positional didn't fix our wickedness.
>> class C1(object):
>> def __init__(self, child_class=None):
>> self.child = child_class(parent=self)
>
> Ditto. None() will raise TypeError
>
Ditto, so would it if your C2 instance is None for whatever reason.
> If your intent is to force passing parent/child_class by name rather
> than by position, use *, as in
> def __init__(self, *, child_class):
>
>
>> def foo(self):
>> print("I am {self.__class__.__name__} foo".format(self=self))
>>
>> c1 = C1(child_class=C2)
>> c1.child.foo() # I am C2 foo
>> # I am C1 foo
>
>
I don't want to force using named params vs keyword params, or I would
have used the asterisk in the first place. I write that code, as I said,
because I have zero to gain from using a positional argument (which
using keyword arguments behaves the same way as far as I'm concerned if
I call the params in order), and a lot to lose: when I change the API,
I'd have to change the whole parts where I instantiate the class or call
the method.
Suppose I have a class like this:
class Foo(object):
def __init__(self, name, height, address):
self.name = name
self.height = height
self.address = address
Suppose I then want to change it to:
class Foo(object):
def __init__(self, status, name, address, height):
self.status = status
self.name = name
self.height = height
self.address = address
This would totally screw up other parts of the code where the class is
used. I tend to use positional arguments when it's *way* unlikely the
code will ever change, and even then I probably wouldn't.
--
~ Jugurtha Hadjar,
More information about the Python-list
mailing list