Python-练习 41. 学着去说面向对象
在这个练习中,我要教你如何去说“面向对象”。我所做的就是给你一些你需要了解的词和定义。
然后我会给出一些需要填空的句子让你去理解。
最后,你要完成一个大练习,从而在大脑中巩固这些句子。
词汇训练
类(class):告诉Python创建一个新类型的东西
tell python to make a new type of thing
对象两种含义(object):最基本类型的东西,任何实例
the most basic type of thing, and any instance of something
实例(instance):当你告诉Python创建一个类的时候你所得到的东西
what you get when you tell python to create a class
def:你如何在类里面定义一个函数
how you define a function inside a class
self:在一个类的函数里面,self是被访问的实例/对象的一个变量
inside the functions in a class, self is a variable for the instance/object being accessed
继承(inheritance):关于一个类能从另一个类那里继承它的特征的概念,很像你和你的父母
the concept that one class can inherit traits from another class,much like you and your parents
组合(composition):关于一个类可以由其他一些类构成的概念,很像一辆车包含几个轮子
the concept that a class can be composed of other classes as parts, much like how a car has wheels
属性(attribute):类所拥有的从组合那里得到的特性,通常是变量
a property classes have that are from composition and are usually variables
is-a:一种用来表达某物继承自一种东西的表述,就像“三文鱼是一种鱼”
a phrase to say that something inherits from another, as in a “salmon” is a “fish”
has-a:一种用来表达某物是由一些东西组成或具有某些特性的表述,就像“三文鱼有一个嘴巴”
a phrase to say that something is composed of other things or has a trait, as in “a salmon has-a mouth”
短语训练
接下来是一些Python代码片段以及解释
class X(Y):
创建一个名为X并继承自Y的类(“Make a class named X that is-a Y.”)
class X(object): def __init__(self, J)
类X有一个带有self和J参数的 _ init_ 函数(“class X has-a _ init_ that takes self and J parameters.”)
class X(object): def M(self, J):
类X有一个带有self和J参数的M函数(“class X has-a function named M that takes self and J parameters.”)
foo = X():
设foo为类X的一个实例(“Set foo to an instance of class X.”)
foo.M(J)
从foo那里获取M函数,并用self和J参数来调用它(“From foo, get the M function, and call it with parameters self, J.”)
foo.K = Q
从foo那里获取K属性,并设它为Q(“From foo, get the K attribute, and set it to Q.”)
在上述每一句中,当你看到X,Y,M,J,K,Q,以及foo,你可以把它们当做空格,比如,我还可以把这些句子写成:
“Make a class named ??? that is-a Y.”
(创建一个名为 ??? 的类,它继承自 Y。)
“class ??? has-a _ init_ that takes self and ??? parameters.”
(类 ??? 有一个带了 self 和 ??? 参数的 _ init_ 。)
“class ??? has-a function named ??? that takes self and ??? parameters.”
(类 ??? 有一个名为 ??? 的函数,这个函数带有 self 和 ??? 两个参数。)
“Set foo to an instance of class ???.”
(设 foo 为类 ??? 的一个实例。)
“From foo, get the ??? function, and call it with self=??? and parameters ???.”
(从 foo 那里获取 ??? 函数,并用 self=??? 以及参数 ??? 来调用它。)
“From foo, get the ??? attribute, and set it to ???.”
(从 foo 那里获取 ??? 属性,把它设为 ???。)
综合训练
最后一项准备工作是把词汇训练和短语训练结合在一起,以下是训练内容:
- 做一个短语卡然后练习记忆。
- 把它翻过来,读句子,如果在句子中看到词汇训练中的词汇,就找到相应的词汇卡片。
- 练习记忆这些词汇卡片。
- 坚持练习,要是你感到有些累,就休息一下再继续。
一个阅读测试
现在我有一个小的 Python 脚本来帮助你掌握这些词汇和短语,并且能够无限运行。
这段脚本很简单,你应该能够看明白,它所做的事情就是用一个叫做 urllib 的图书馆来下载一列单词。
以下是脚本代码,你需要输入到 oop_test.py 这个文件里来使用:
oop_test.py(原代码)
import random
from urllib.request import urlopen
import sys
WORD_URL = "http://learncodethehardway.org/words.txt"
WORDS = []
PHRASES = {
"class %%%(%%%):":
"Make a class named %%% that is-a %%%.",
"class %%%(object):\n\tdef __init__(self, ***)" :
"class %%% has-a __init__ that takes self and *** params.",
"class %%%(object):\n\tdef ***(self, @@@)":
"class %%% has-a function *** that takes self and @@@ params.",
"*** = %%%()":
"Set *** to an instance of class %%%.",
"***.***(@@@)":
"From *** get the *** function, call it with params self @@@.",
"***.*** = '***'":
"From *** get the *** attribute and set it to '***'."
}
# do they want to drill phrases first
if len(sys.argv) == 2 and sys.argv[1] == "english":
PHRASE_FIRST = True
else:
PHRASE_FIRST = False
# load up the words from the website
for word in urlopen(WORD_URL).readlines():
WORDS.append(str(word.strip(), encoding="utf-8"))
def convert(snippet, phrase):
class_names = [w.capitalize() for w in
random.sample(WORDS, snippet.count("%%%"))]
other_names = random.sample(WORDS, snippet.count("***"))
results = []
param_names = []
for i in range(0, snippet.count("@@@")):
param_count = random.randint(1,3)
param_names.append(', '.join(random.sample(WORDS, param_count)))
for sentence in snippet, phrase:
result = sentence[:]
# fake class names
for word in class_names:
result = result.replace("%%%", word, 1)
# fake other names
for word in other_names:
result = result.replace("***", word, 1)
# fake parameter lists
for word in param_names:
result = result.replace("@@@", word, 1)
results.append(result)
return results
# keep going until they hit CTRL-D
try:
while True:
snippets = list(PHRASES.keys())
random.shuffle(snippets)
for snippet in snippets:
phrase = PHRASES[snippet]
question, answer = convert(snippet, phrase)
if PHRASE_FIRST:
question, answer = answer, question
print(question)
input("> ")
print(f"ANSWER: {answer}\n\n")
except EOFError:
print("\nBye")
ex41.py (oop_test.py注释、中文、监视语句)
代码
import random#调用random模块
from urllib.request import urlopen#负责打开浏览url内的html 文本
import sys#调用sys模块
WORD_URL = "http://learncodethehardway.org/words.txt"
WORDS = []#创建列表
PHRASES = {#创建字典
"class %%%(%%%):":
"创建一个名为%%%并继承于%%%的类",
#"Make a class named %%% that is a %%%.",
"class %%%(object):\n\tdef __init__(self, ***)":
"类%%%有一个带self和***的参数__init__",
#"class %%% has-a __init__ that takes self and *** params.",
"class %%%(object):\n\tdef ***(self, @@@)":
"类%%%有一个名为***的函数,这个函数带有self和@@@两个参数",
#"class %%% has-a function *** that takes self and @@@ params.",
"*** = %%%()":
"设***为类%%%的一个实例",
#"Set *** to an instance of class %%%.",
"***.***(@@@)":
"从***实例那里获取***函数,并用self=@@@并调用它",
#"From *** get the *** function, call it with params self @@@.",
"***.*** = '***'":
"从***实例那里获取***属性,把它设为***"
#"From *** get the *** attribute and set it to '***'."
}
# do they want to drill phrases first,他们想先联系短语吗
if len(sys.argv) == 2 and sys.argv[1] == "english":#检查输入长度为2及第二个词为english
PHRASE_FIRST = True
else:
PHRASE_FIRST = False
print("检查>>>>",PHRASE_FIRST)
#load up the words from the website,从网站上加载单词
for word in urlopen(WORD_URL).readlines():
WORDS.append(str(word.strip(), encoding = "utf-8"))
print("WORDS列表内容>>>>",repr(WORDS))
#去除首尾空格转换成字符串格式加入到WORDS序列中
def convert(snippet, phrase):
class_names = [w.capitalize() for w in#capitalize()将字符串的第一个字母变成大写,其他字母变小写。
random.sample(WORDS, snippet.count("%%%"))]#从WORDS序列中随机取出与%%%的个数相同单词,首字母大写后赋值给class_names
print("类名>>>>", repr(class_names))
other_names = random.sample(WORDS,snippet.count("***"))#从WORDS中随机获取个数与***的个数相同单词赋值给other_names
print("其他名>>>>", repr(other_names))
results = []
param_names = []
for i in range(0, snippet.count("%%%")):#只循环一次
param_count = random.randint(1,3)#随机生成1-3之间的数字,包含1和3
print("1-3内取得随机数>>>", repr(param_count))
param_names.append(', '.join(#从WORDS中随机取出?个单词用逗号和空格连接起来,放在param_names中
random.sample(WORDS,param_count)))
print("WORDS中获取随机名称>>>>", repr(param_names))
for sentence in snippet, phrase:#循环两次
result = sentence[:]#result是列表,sentence是列表,sentence[:]是列表切片意思是取其所有项
# replace() 方法把字符串中的 old(旧字符串) 替换成 new(新字符串),如果指定第三个参数max,则替换不超过 max 次。
#fake class names
for word in class_names:#将%%%替换为Word,只替换一次
result = result.replace("%%%", word, 1)
print("加入结果类名>>>>", repr(result))
# fake other names
for word in other_names:#将***替换为Word,只替换一次
result = result.replace("***", word, 1)
print("加入其他名称>>>>", repr(result))
#fake parameter lists
for word in param_names:#将@@@替换为Word,只替换一次
result = result.replace("@@@", word, 1)
print("加入参数名>>>>", repr(result))
results.append(result)
print("最终结果>>>>", repr(results))
return results
#keep going until they hit CTRL-D,继续进行直到输入Ctrl-D
try:#异常捕获处理
while True:
snippets = list(PHRASES.keys())#keys() 函数以列表返回一个字典所有的键
print("字典的键>>>>",repr(snippets))
random.shuffle(snippets)#shuffle() 方法将序列的所有元素随机排序
print("键随机排序后>>>>",repr(snippets))#不会生成新的列表,只是将原列表的次序打乱
for snippet in snippets:
phrase = PHRASES[snippet]
question, answer = convert(snippet, phrase)#调用convert函数,传参,得到返回值
if PHRASE_FIRST:
question, answer = answer, question
print(question)
input("> ")
print(f"结果: {answer}\n\n")
except EOFError:#如果在try部份引发了EOFError异常
print("\nBye")
运行结果
记住,这些短语在用一些废话,学习阅读这些代码的一部分原因就是试着不再去给这些变量和类的名字赋予这么多意义。通常当人们看到像“cork”(软木塞)这样的词时,会对它的含义感到很困惑。在上述例子中,“cork”只是一个随机选取的类的名字。别给它赋予太多含义,而是试着用我教你的方式来对待它。
读更多代码
你现在需要继续读更多的代码,并在这些代码中复习你之前学过的短语。试着找到尽可能多的包含类的文件,然后跟着如下要求去做:
- 给出每个类的名字,以及其他的类从它那里继承了什么。
- 在每个类下面,列出它所拥有的函数以及它们的参数。
- 列出所有它用 self 使用的属性。
- 对于每个属性,给出它继承自哪个类。
这些练习的目的是过一遍真实的代码,并试着把你学过的短语和它们的用法匹配和关联起来。如果你做足了训练,你会开始看到这些匹配模式(match patterns)呼之欲出,而不再是一些你不明白的空格或字符。
问题
result = sentence[:] 是干什么用的?
这是 Python 复制一个列表的方式。它用的是列表的切片(slice)语法 [:] ,能够很快地创建一个从第一个元素到最后一个元素的列表切片。
这个脚本好难运行!
到目前为止你应该能够让它正常运行。虽然它确实有几个小地方比较烦人,但是并不复杂。试着用你目前为止所学过的东西来调试它。把每一行输入进去,并且确保和我的一模一样,然后遇到不明白的地方就在网上查查。
还是很难!
你可以这样做。慢点敲,一个字符一个字符地敲,但是要保证准确,然后弄明白每个词的意思。