What I do when imitating go embedding in python, or rather delegation rather than inheritance.
One of the reasons why inheritance is painful is that you don't know which method was called.
For example, for C with the following inheritance relationship
class A:
def f(self):
return "f"
def g(self):
return "g"
class B:
def h(self):
return "h"
def i(self):
return "i"
class C(A, B):
def m(self):
return (self.f(), self.i())
It is troublesome to understand the processing behavior of C # m ()
. Of course, I think I'm usually a little more particular about the name, so I don't think it should be so bad.
On the other hand, if it is a delegation, it is still better.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
This is because it will be specified which method of the instance variable that is held internally is called.
Here is the embedding of go. This can also be seen as a feature that does the delegation semi-automatically.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
ReadeWriter has Reader and Writer functions. In essence, it just delegates to the value it has inside. The above example was an interface example, but the same is true for structs.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
If you do something like rw.Read ()
for a variable rw that becomes var rw * ReadWriter
here, then something likerw.Reader.Read ()
will be called.
Go back to python. You mentioned in the first example that delegation is better than inheritance. However, it was not completely resolved by delegation. You may want to call methods A and B directly from C. Also, if you do something like c.a.f ()
directly where you use C, the values that can actually be used will be fixed to the instance of C.
That's why it happens that you want c.a.f ()
to be called internally while calling c.f ()
. That said, it's hard to explicitly write the required method invocation each time. I would be happy if you could do it automatically. In other words, it is an imitation of embedding go.
The main subject is from here, but if you want to imitate the embedding of go in python, I personally do as follows.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
return getattr(self.a, name)
This will automatically change c.f
to c.a.f
. This doesn't completely mimic the embedding of go. If an attribute that does not exist is specified, it will all be delegated to self.a. It can be done a little more explicitly or more restrictively by using metaprogramming etc. I think this is enough for now.
In the original example, there were multiple inheritance sources. In the previous example, we can only handle the case where there is only one inheritance source. Let's look at multiple cases as well. The point to note is how to use getattr. You can specify the default value in the third argument of getattr. Try to return some special value instead of None. This is because even if None is set, it will search for the next search destination (on the contrary, if you want to search for another candidate if there is None, you can do so. Understand the behavior. I don't recommend it because it will be difficult to do).
missing = object()
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
v = getattr(self.a, name, missing)
if v is not missing:
return v
return getattr(self.b, name)
Now c.i ()
will call c.b.i ()
and c.f ()
will call c.a.f ()
.
By the way, in the original example, I wrote an example in which the value inheriting multiple A and B is supported by multiple transfer destinations in the final example. In actual code, it is not recommended to make such an implicit transfer to multiple transfer destinations. This is because it becomes difficult to grasp the behavior by looking at the putt. Therefore, it is better to limit the transfer destination to only one as much as possible.
Recommended Posts