What I do when imitating embedded go in python

Introduction

What I do when imitating go embedding in python, or rather delegation rather than inheritance.

The story that inheritance is difficult

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.

embedding go

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.

Imitation of embedding in python

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.

When there are multiple inheritance sources

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

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

What I do when imitating embedded go in python
[Question] What happens when I use% in python?
What I learned in Python
I want to do something in Python when I finish
What to do when "SSL: CERTIFICATE_VERIFY_FAILED _ssl.c: 1056" appears in Python
What to do when ModuleNotFoundError: No module named'XXX' occurs in Python
What to do when the value type is ambiguous in Python?
After all, what should I use to do type comparisons in Python?
I want to do Dunnett's test in Python
[Python] What I did to do Unit Test
When I try matplotlib in Python, it says'cairo.Context'
What I did when updating from Python 2.6 to 2.7
What should I do with DICOM in MPEG2?
What to do to get google spreadsheet in python
[python] What to do when an error occurs in send_keys of headless chrome
What to do when a warning appears around Python integration in Neovim's CheckHealth
What I did when I got stuck in the time limit with lambda python
What to do when [Errno 2] No such file or directory appears in Python
[Go 1.13] What to do when unexpected directory layout: appears
[openpyxl] What to do when IllegalCharacterError appears in pandas.DataFrame.to_excel
What to do when "cannot import name xxx" [Python]
What I was addicted to when using Python tornado
What to do when you can't bind CaboCha to Python
What to do if you get an error when importing matplotlib in Python (Mac)
What to do when Python starts up in Anaconda does not come out unexpectedly
I wrote python in Japanese
Attention when os.mkdir in Python
I understand Python in Japanese!
What to do if ʻarguments [0] .scrollIntoView ();` fails in python selenium
What I got stuck around GUI in WSL python environment
I want to do something like sort uniq in Python
What I was addicted to when migrating Processing users to Python
What to do when "Invalid HTTP_HOST header" appears in Django
What I was asked when using Random Forest in practice
What to do when there is no response due to Proxy setting in Python web scraping
What to do when Ubuntu crashes
[Python] How to do PCA in Python
Precautions when using pit in Python
What to do if you get a minus zero in Python
Behavior when listing in Python heapq
I got an AttributeError when mocking the open method in python
What I was addicted to when introducing ALE to Vim for Python
What I was addicted to with json.dumps in Python base64 encoding
I wrote Fizz Buzz in Python
What to do with PYTHON release?
What to do if Insecure Platform Warning appears when running Python
I tried Grumpy (Go running Python).
if True> = False: Python "True" Go "What ???"
I wanted to do something like an Elixir pipe in Python
I learned about processes in Python
I can't install scikit-learn in Python
Scribble what I used when using ipython in formatting pos data
I get a can't set attribute when using @property in python
I wrote the queue in Python
What I did when I wanted to make Python faster -Numba edition-
What to do when you get "I can't see the site !!!!"
What to do when UnicodeDecodeError occurs during read_csv in pandas (pd.read_table ())
What automation should I do in RPA, VBA, and programming languages?
I tried Line notification in Python
NameError: global name'dot_parser' is not defined and what to do when it comes up in python
When using regular expressions in Python