1. 程式人生 > 實用技巧 >Learn python the hard way(ex44) 繼承和組合 Inheritance versus Composition

Learn python the hard way(ex44) 繼承和組合 Inheritance versus Composition

When you are doing this kind of specialization, there are three ways that the parent and child classes can interact:
# 當你在做這種專業化的工具的時候, 有三種父類和子類可以繼承的方式.
\1. Actions on the child imply an action on the parent.
# 在子類上的操作意味著對父類的操作
\2. Actions on the child override the action on the parent.
# 在子類上的操作會覆蓋在父類上的操作
\3. Actions on the child alter the action on the parent.
# 在子類上的操作會改變父類上的操作
I will now demonstrate each of these in order and show you code for them.
# now I will (in order) demonstrate each of these
# 現在我會依次演示它們中的每一個,並給你看它們的程式碼

Implicit Inheritance 隱式繼承

First I will show you the implicit actions that happen when you define a function in the parent but *not* in the child.
# 首先我會向你展示當你定義一個方法在父函式中而不是子函式時會發生的隱式操作.
class Parent(object):
    # 隱式
    def implicit(self):
        print("PARENT implicit()")

class Child(Parent):
    pass

dad = Parent()
son = Child()

dad.implicit()
son.implicit()

The use of pass under the class Child: is how you tell Python that you want an empty block. 
# 在類Child的下方使用pass: 這正是你告訴python你想要一個空模組

This creates a class named Child but says that there’s nothing new to define in it. 
# 這裡建立了一個新的類叫做Child, 但其中並沒有任何新的東西需要定義

Instead it will inherit all of its behavior from Parent. 
# 反而,他會從Parent繼承所有的行為.

When you run this code you get the following:
# 當你執行這些程式碼的時候你會得到如下結果:
$ python3.6 ex44a.py
PARENT implicit()
PARENT implicit()
Notice how even though I’m calling son.
# 注意看我即使是正在呼叫son例項

implicit() on line 13 and even though Child does not have an implicit function defined, 
# 第13行中的implicit() 即使Child沒有一個隱式的方法被定義

it still works, and it calls the one defined in Parent. 
# 他仍然起作用了, 並且它呼叫了Parent中被定義的方法

This shows you that if you put functions in a base class (i.e., Parent), 
# 這表明 如果你把方法放進一個諸如(Parent())的類中,

then all subclasses (i.e., Child) will automatically get those features. 
# 那麼所有的子類,比如(Child)會自動獲得這些功能,

Very handy for repetitive code you need in many classes.
# 當你需要在類中寫很多重複性的程式碼時非常有用

Override Explicitly 顯式重寫

The problem with having functions called implicitly is sometimes you want the child to behave differently.
# 有時你想要child表現得不同一些,這裡會有一個隱式呼叫的問題
# 隱式呼叫函式的問題在於,有時你想要child表現得不同

In this case you want to override the function in the child, 
# 在這個例子中,你想要的覆蓋child中的函式

effectively replacing the functionality.
# 有效地替換函式

To do this just define a function with the same name in Child. Here’s an example:
# 想要做到它,只需要定義一個在Child類中有相同名字的函式. 這裡有一個例子:
class Parent(object):

    def override(self):
        print("PARENT override()")

class Child(Parent):

    def override(self):
        print("Child override()")

dad = Parent()
son = Child()

dad.override()
son.override()
(venv) C:\work>python ex44b.py
PARENT override()
Child override()
As you can see, when line 14 runs, it runs the Parent.
# 你可以看到, 當地十四行執行的時候,它運行了Parent類

override function because that variable (dad) is a Parent.
# 覆蓋了方法,因為變數dad是類的例項

But when line 15 runs, it prints out the Child.
# 但是當執行到15行時, 它打印出了Child

override messages because son is an instance of Child and Child overrides that function by defining its own version.
# 覆蓋了資訊.因為son是Child中的一個例項.並且Child通過定義他自己的方法,覆蓋了原本的函式

Take a break right now and try playing with these two concepts before continuing
# 休息一下現在, 並在繼續之前嘗試瞭解以下兩個概念

Alter Before or After

The third way to use inheritance is a special case of overriding where you want to alter the behavior before
or after the Parent class’s version runs.
# 使用繼承的第三種方法是覆蓋的特殊情況,即您希望在父類版本執行之前或之後更改行為。

You first override the function just like in the last example, but
then you use a Python built-in function named super to get the Parent version to call.
# 首先,就像在上一個例子中,你覆蓋了函式,但之後你使用了python的一個名叫super內建函式來呼叫父版本.


Here’s the example of doing that so you can make sense of this description:
# 下面是一個例子,這樣你就能理解這個描述了:
class Parent(object):
    # 父類有一個方法叫altered
    def altered(self):
        print("PARENT altered()")

class Child(Parent):
    # 子類也有一個方法叫altered
    def altered(self):
        print("CHILD, BEFORE PARENT altered()")
        super(Child, self).altered()
        print("CHILD, AFTER PARENT altered()")

dad = Parent()
son = Child()

dad.altered()
son.altered()

The important lines here are 9–11, where in the Child I do the following when son.altered() is called:

#  9-11行比較重要,  在Child中,當我呼叫son.altered()時,執行以下操作:
1. Because I’ve overridden Parent.altered the Child.altered version runs, and line 9 executes like you’d expect.
# 
2. In this case I want to do a before and after, so after line 9 I want to use super to get the Parent.altered version.

3. On line 10 I call super(Child, self).altered(), which is aware of inheritance and will get the Parent class for you. 

You should be able to read this as “call super with arguments Child and self, then call the function altered on whatever it returns.”
#  你應該把他理解為 :使用引數Child和self來執行super,然後呼叫altered(),不管他返回什麼

4. At this point, the Parent.altered version of the function runs, and that prints out the Parent
message.

5. Finally, this returns from the Parent.altered, and the Child.altered function continues
to print out the after message.



If you run this, you should see this:

(venv) C:\work>python ex44c.py
PARENT altered()

CHILD, BEFORE PARENT altered()
PARENT altered()
CHILD, AFTER PARENT altered()

All Three Combined

To demonstrate all of these, I have a final version that shows each kind of interaction from inheritance in one file:

ex44d.py

class Parent(object):
    # 父類的override方法
    def override(self):
        print("PARENT override()")
    # 父類的implicit方法 # 未被子類覆蓋
    def implicit(self):
        print("PARENT implicit()")
    # 父類的altered方法
    def altered(self):
        print("PARENT altered()")

# 子類
class Child(Parent):
    # 子類的override方法 覆蓋了父類的override
    def override(self):
        print("CHILD override()")
    # 子類的altered方法 覆蓋了父類的altered方法
    def altered(self):
        print("CHILD, BEFORE PARENT altered()")
        # 繼承了父類的altered()方法
        super(Child, self).altered()
        print("CHILD, AFTER PARENT altered()")

# 例項生成
dad = Parent()
son = Child()

# 呼叫方法
dad.implicit()
son.implicit() # 未被覆蓋

dad.override()
son.override() # 覆蓋了

dad.altered()
son.altered() # 覆蓋了dad得altered()方法 用super繼承

Go through each line of this code, and write a comment explaining what that line does and whether it’s
an override or not. 
Then run it and confirm you get what you expected
# 遍歷每一行的程式碼, 併為這一行做了什麼和是否它覆蓋了寫一段註釋.
# 然後執行它 並確認你得到了你所期望的

(venv) C:\work>python ex44d.py
PARENT implicit()
PARENT implicit()
PARENT override()
CHILD override()
PARENT altered()
CHILD, BEFORE PARENT altered()
PARENT altered()
CHILD, AFTER PARENT altered()

The Reason for super()

This should seem like common sense, but then we get into trouble with a thing called multiple inheritance.
# 這似乎是常識, 但隨後我們遇到了一個叫做多繼承的麻煩

Multiple inheritance is when you define a class that inherits from one or more classes, like this:
# 多繼承是當你定義一個繼承自一個或多個類的clas時,像這樣:

class SuperFun(Child, BadStuff):
	pass
This is like saying, “Make a class named SuperFun that inherits from the classes Child and BadStuff at the same time.”
這就好像在說, "建立一個叫SuperFun的類 同時繼承Child和BadStuff類"

In this case, whenever you have implicit actions on any SuperFun instance,
# 在這種情況下, 無論你在SuperFun的例項上有多少個隱式動作時

 Python has to look up the possible function in the class hierarchy for both Child and BadStuff,
Python必須在類層次結構中查詢可能的函式,包括Child和bad,

but it needs to do this in a consistent order. 
# 但是它需要按照一致的順序來做

do this Python uses “method resolution order” (MRO) and an algorithm called C3 to get it straight.
# 為了做到這一點,Python使用了“方法解析順序”(MRO)和一個稱為C3的演算法


Because the MRO is complex and a well-defined algorithm is used, Python can’t leave it to you to get the MRO right.

Instead, Python gives you the super() function, which handles all of this for you in the places that you need the altering type of actions as I did in Child.altered.

With super() you don’t have to worry about getting this right, and Python will find the right function for you.

因為MRO是複雜的,並且使用了定義良好的演算法,所以Python不能讓您來獲得正確的MRO。

相反,Python提供了super()函式,它在需要更改操作型別的地方為您處理所有這些操作,就像我在child.altered 中所做的那樣。

使用super(),您不必擔心如何正確地實現這個函式,Python將為您找到正確的函式

Using super() with init

The most common use of super() is actually in __init__ functions in base classes. This is usually
the only place where you need to do some things in a child, then complete the initialization in the parent.
Here’s a quick example of doing that in the Child:

實際上,super()最常見的用法是在基類中的剩餘init__函式中。
這通常是惟一需要在子程式中執行某些操作,然後在父程式中完成初始化的地方。
這裡有一個在孩子身上做的簡單例子:
class Child(Parent):
def __init__ (self, stuff):
	self.stuff = stuff
	super(Child, self).__init__()

Composition

Inheritance is useful, but another way to do the exact same thing is just to use other classes and modules,
rather than rely on implicit inheritance. If you look at the three ways to exploit inheritance, two of the three
involve writing new code to replace or alter functionality. This can easily be replicated by just calling
functions in a module. Here’s an example of doing this:

繼承是有用的,但是做同樣事情的另一種方法是僅僅使用其他類和模組,而不是依賴隱式繼承。
如果您檢視利用繼承的三種方法,其中兩種涉及編寫新程式碼來替換或更改功能。
只需呼叫模組中的函式就可以輕鬆地複製這一點。
這裡有一個這樣做的例子:

ex44e.py

class Other(object):

    def override(self):
        print("OTHER override()")

    def implicit(self):
        print("OTHER implicit()")

    def altered(selfs):
        print("OTHER altered()")

class Child(object):

    # 自帶屬性
    def __init__(self):
        # o生成一個例項叫other 作為屬性,賦值給Child例項
        self.other = Other()

    # 定義方法implict() 覆蓋父類implict()
    def implicit(self):
        # 呼叫child例項的other例項的方法implicit()
        self.other.implicit()

    # 方法覆蓋父類的override
    def override(self):
        print("CHILD override()")

    # 覆蓋父類
    def altered(self):
        print("CHILD, BEFORE OTHER altered()")
        # 呼叫 other例項的altered()方法
        self.other.altered()
        print("CHILD, AFTER OTHER altered()")


son = Child()

son.implicit()
son.override()
son.altered()

In this code I’m not using the name Parent, since there is not a parent-child is-a relationship. This is
a has-a relationship, where Child has-a Other that it uses to get its work done. When I run this I get
the following output:

在此程式碼中,我沒有使用名稱Parent,因為沒有父子is-a關係。這是一個has-a關係,其中Child有一個Other來完成它的工作。當我執行這個,我得到如下輸出:

(venv) C:\work>python ex44e.py
OTHER implicit()
CHILD override()
CHILD, BEFORE OTHER altered()
OTHER altered()
CHILD, AFTER OTHER altered()
You can see that most of the code in Child and Other is the same to accomplish the same thing. 

The only difference is that I had to define a Child.implicit function to do that one action.

I could then ask myself if I need this Other to be a class, and could I just make it into a module named other.py?

您可以看到,Child和Other中的大部分程式碼都是相同的,可以完成相同的事情。
唯一的區別是我必須定義一個孩子。隱函式來做這個動作。

然後我可以問自己,我是否需要這個其他的是一個類,我是否可以把它放到一個名為Other .py的模組中?

When to Use Inheritance or Composition

The question of “inheritance versus composition” comes down to an attempt to solve the problem of
reusable code. You don’t want to have duplicated code all over your software, since that’s not clean and
efficient. Inheritance solves this problem by creating a mechanism for you to have implied features in
base classes. Composition solves this by giving you modules and the capability to call functions in other
classes.

繼承還是組合”的問題歸結為試圖解決可重用程式碼的問題。您不希望在您的軟體上到處都有重複的程式碼,因為這樣做不乾淨,也不高效。
繼承通過在基類中建立一種具有隱含特性的機制來解決這個問題。
複合為您提供了模組和呼叫其他類中的函式的能力,從而解決了這個問題.
If both solutions solve the problem of reuse, then which one is appropriate in which situations? The
answer is incredibly subjective, but I’ll give you my three guidelines for when to do which:

如果這兩種解決方案都解決了重用的問題,那麼哪種方案在哪種情況下合適呢?這個問題的答案是非常主觀的,但是我會告訴你我的三個指導原則:
1. Avoid multiple inheritance at all costs, as it’s too complex to be reliable. If you’re stuck with
it, then be prepared to know the class hierarchy and spend time finding where everything is
coming from.
2. Use composition to package code into modules that are used in many different unrelated places
and situations.
3. Use inheritance only when there are clearly related reusable pieces of code that fit under a
single common concept or if you have to because of something you’re using.

#1. 儘量避免多重繼承,因為它太複雜而不可靠。如果您被它困住了,那麼請準備好了解類層次結構並花時間查詢所有內容的來源
#2.使用組合將程式碼打包到模組中,用於許多不同的不相關的地方和情況。
#3. 只有當有明確相關的可重用程式碼片段符合單一公共概念時,或者當你不得不使用某些東西時,才使用繼承。
Do not be a slave to these rules. The thing to remember about object-oriented programming is that it
is entirely a social convention programmers have created to package and share code. Because it’s a
social convention, but one that’s codified in Python, you may be forced to avoid these rules because of
the people you work with. In that case, find out how they use things and then just adapt to the situation.

不要做這些規則的奴隸。關於面向物件程式設計,需要記住的是,它完全是程式設計師為打包和共享程式碼而建立的一種社會慣例。因為這是一種社會慣例,但是是用Python編寫的,所以您可能會因為與您一起工作的人而被迫避免這些規則。在這種情況下,找出他們如何使用事物,然後適應環境

Study Drills

There is only one Study Drill for this exercise because it is a big exercise. Go and read http://www

.python.org/dev/peps/pep-0008/ and start trying to use it in your code. You’ll notice that some of it is

different from what you’ve been learning in this book, but now you should be able to understand their

recommendations and use them in your own code. The rest of the code in this book may or may not

follow these guidelines depending on whether it makes the code more confusing. I suggest you also do

this, as comprehension is more important than impressing everyone with your knowledge of esoteric

style rules.

這個練習只有一個學習練習,因為它是一個大練習。去看看http://www
。python.org/dev/peps/pep-0008/,並開始嘗試在程式碼中使用它。您會注意到其中一些內容與您在本書中所學的內容有所不同,但是現在您應該能夠理解他們的建議並在自己的程式碼中使用它們。本書的其餘程式碼是否遵循這些指導原則取決於它是否使程式碼更加混亂。我建議你也這樣做,因為理解比用你的e知識給別人留下深刻印象更重要

Common Student Questions

我怎樣才能更好地解決以前沒有遇到過的問題呢?

How do I get better at solving problems that I haven’t seen before?

The only way to get better at solving problems is to solve as many problems as you can by yourself. Typically people hit a difficult
problem and then rush out to find an answer. This is fine when you have to get things done, but
if you have the time to solve it yourself, then take that time. Stop and bang your head against
the problem for as long as possible, trying every possible thing, until you solve it or give up.
After that the answers you find will be more satisfying, and you’ll eventually get better at solving
problems.

更好地解決問題的唯一方法就是自己解決儘可能多的問題。通常人們遇到一個難題,然後衝出去尋找答案。當你必須把事情做完的時候,這很好,但是如果你有時間自己解決它,那麼就花時間。停下來,儘可能長時間地用你的腦袋去思考這個問題,嘗試所有可能的事情,直到你解決了它或者放棄。
在那之後,你找到的答案會更令人滿意,最終你會更善於解決問題

Aren’t objects just copies of classes?

In some languages (like JavaScript) that is true. These are
called prototype languages, and there are not many differences between objects and classes
other than usage. In Python, however, classes act as templates that “mint” new objects, similar
to how coins are minted using a die (template).

物件不就是類的副本嗎?

在某些語言中(如JavaScript)是這樣的。這些被稱為原型語言,除了用法之外,物件和類之間沒有太多區別。然而,在Python中,類充當“鑄造”新物件的模板,類似於使用die(模板)鑄造硬幣。