当前位置: 首页 > news >正文

阿里云备案成功怎么建设网站seo分析报告

阿里云备案成功怎么建设网站,seo分析报告,品牌推广策划方案怎么写,网站的流量是怎么回事原文#xff1a;http://inventwithpython.com/beyond/chapter16.html 定义一个函数#xff0c;并从几个地方调用它#xff0c;可以省去复制和粘贴源代码的麻烦。不复制代码是一个很好的实践#xff0c;因为如果你需要修改它#xff08;无论是为了修复一个错误还是添加新特… 原文http://inventwithpython.com/beyond/chapter16.html 定义一个函数并从几个地方调用它可以省去复制和粘贴源代码的麻烦。不复制代码是一个很好的实践因为如果你需要修改它无论是为了修复一个错误还是添加新特性你只需要在一个地方修改它。没有重复的代码程序也更短更容易阅读。 类似于函数继承是一种代码重用技术您可以将其应用于类。这是将类置于父子关系中的行为在这种关系中子类继承父类方法的副本使您不必在多个类中复制一个方法。 许多程序员认为继承被高估了甚至是危险的因为大量的继承类增加了程序的复杂性。以“继承是邪恶的”为标题的博客文章并非完全不着边际继承当然容易被过度使用。但是在组织代码时有限地使用这种技术可以节省大量时间。 继承是如何工作的 要创建一个新的子类可以将现有父类的名称放在class语句的括号中。要练习创建子类请打开一个新的文件编辑器窗口并输入以下代码保存为inheritanceExample.py : class ParentClass: # 1def printHello(self): # 2print(Hello, world!)class ChildClass(ParentClass): # 3def someNewMethod(self):print(ParentClass objects dont have this method.)class GrandchildClass(ChildClass): # 4def anotherNewMethod(self):print(Only GrandchildClass objects have this method.)print(Create a ParentClass object and call its methods:) parent ParentClass() parent.printHello()print(Create a ChildClass object and call its methods:) child ChildClass() child.printHello() child.someNewMethod()print(Create a GrandchildClass object and call its methods:) grandchild GrandchildClass() grandchild.printHello() grandchild.someNewMethod() grandchild.anotherNewMethod()print(An error:) parent.someNewMethod()运行该程序时输出应该如下所示 Create a ParentClass object and call its methods: Hello, world! Create a ChildClass object and call its methods: Hello, world! ParentClass objects dont have this method. Create a GrandchildClass object and call its methods: Hello, world! ParentClass objects dont have this method. Only GrandchildClass objects have this method. An error: Traceback (most recent call last):File inheritanceExample.py, line 35, in moduleparent.someNewMethod() # ParentClass objects dont have this method. AttributeError: ParentClass object has no attribute someNewMethod我们创建了三个名为ParentClass 1 、ChildClass 3 和GrandchildClass 4 的类。ChildClass 子类 ParentClass意味着ChildClass将拥有与ParentClass相同的所有方法。我们说ChildClass 继承了ParentClass的方法。另外GrandchildClass继承了ChildClass的子类因此它拥有与ChildClass及其父类ParentClass相同的所有方法。 使用这种技术我们已经有效地将printHello()方法 2 的代码复制并粘贴到了ChildClass和GrandchildClass类中。我们对printHello()中代码的任何更改不仅会更新ParentClass还会更新ChildClass和GrandchildClass。这与更改函数中的代码会更新其所有函数调用是一样的。你可以在图 16-1 中看到这个关系。注意在类图中箭头是从子类指向基类的。这反映了一个事实即一个类总是知道它的基类但不知道它的子类。 图 16-1一个层次图左和文氏图右显示了三个类和它们拥有的方法之间的关系 人们常说父子类代表“是一种”关系。一个ChildClass对象是一个ParentClass对象因为它拥有与一个ParentClass对象相同的所有方法包括一些它定义的额外方法。这种关系是单向的ParentClass对象不是ChildClass对象。如果一个ParentClass对象试图调用someNewMethod()它只存在于ChildClass对象以及ChildClass的子类Python 会抛出一个AttributeError。 程序员通常认为相关的类必须适应现实世界中的“是”层次结构。OOP 教程一般有VehicleFourWheelVehicle▶CarAnimalBirdSparrow或者ShapeRectangleSquare的父类、子类、孙类。但是请记住继承的主要目的是代码重用。如果您的程序需要一个具有一组方法的类这些方法是其他类的方法的完全超集继承允许您避免复制和粘贴代码。 我们有时也称子类为子类或派生类称父类为超类或基类。 覆盖方法 子类继承其父类的所有方法。但是子类可以通过用自己的代码提供自己的方法来覆盖继承的方法。子类的覆盖方法将与父类的方法同名。 为了说明这个概念让我们回到上一章创建的井字棋。这一次我们将创建一个新类MiniBoard它继承了TTTBoard并覆盖了getBoardStr()以提供一个更小的井字棋棋盘。程序会询问玩家使用哪种风格的棋盘。我们不需要复制和粘贴其余的TTTBoard方法因为MiniBoard将继承它们。 将以下内容添加到您的tictactoe_oop.py文件的末尾以创建原始TTTBoard类的子类然后覆盖getBoardStr()方法 class MiniBoard(TTTBoard):def getBoardStr(self):Return a tiny text-representation of the board.# Change blank spaces to a .for space in ALL_SPACES:if self._spaces[space] BLANK:self._spaces[space] .boardStr f{self._spaces[1]}{self._spaces[2]}{self._spaces[3]} 123{self._spaces[4]}{self._spaces[5]}{self._spaces[6]} 456{self._spaces[7]}{self._spaces[8]}{self._spaces[9]} 789# Change . back to blank spaces.for space in ALL_SPACES:if self._spaces[space] .:self._spaces[space] BLANKreturn boardStr与TTTBoard类的getBoardStr()方法一样MiniBoard的getBoardStr()方法创建了一个井字棋棋盘的多行字符串在传递给print()函数时显示。但是这个字符串要小得多放弃了 X 和 O 标记之间的线使用点号来表示空格。 更改main()中的行使其实例化一个MiniBoard对象而不是一个TTTBoard对象 if input(Use mini board? Y/N: ).lower().startswith(y):gameBoard MiniBoard() # Create a MiniBoard object.else:gameBoard TTTBoard() # Create a TTTBoard object.除了对main()的这一行修改程序的其余部分和以前一样。当您现在运行该程序时输出将如下所示 Welcome to Tic-Tac-Toe! Use mini board? Y/N: y... 123... 456... 789 What is Xs move? (1-9) 1X.. 123... 456... 789 What is Os move? (1-9) --snip--XXX 123.OO 456O.X 789 X has won the game! Thanks for playing!您的程序现在可以轻松地拥有这两个井字棋棋盘类的实现。当然如果你只想要迷你版的板你可以简单地替换TTTBoard的getBoardStr()方法中的代码。但是如果你需要和继承可以让你通过重用它们的公共代码轻松地创建两个类。 如果我们不使用继承我们可以给TTTBoard添加一个名为useMiniBoard的新属性并在getBoardStr()中放置一个if-else语句来决定何时显示常规面板或迷你面板。对于这样一个简单的改变这将很好地工作。但是如果MiniBoard子类需要覆盖 2、3 甚至 100 个方法呢如果我们想创建几个不同的TTTBoard子类会怎么样不使用继承会导致我们的方法中的if-else语句爆炸并大大增加代码的复杂性。通过使用子类和覆盖方法我们可以更好地将代码组织成单独的类以处理这些不同的用例。 super()函数 子类的覆盖方法通常类似于父类的方法。尽管继承是一种代码重用技术但覆盖方法可能会导致您覆盖父类方法中的相同代码作为子类方法的一部分。为了防止这种重复代码内置的super()函数允许一个覆盖方法调用父类中的原始方法。 例如让我们创建一个名为HintBoard的新类它是TTTBoard的子类。新的类覆盖了getBoardStr()所以在画了井字棋棋盘之后它还添加了一个提示如果 X 或 O 能在他们的下一步棋中获胜。这意味着HintBoard类的getBoardStr()方法必须做所有与TTTBoard类的getBoardStr()方法绘制井字棋棋盘相同的任务。我们可以使用super()从HintBoard类的getBoardStr()方法中调用TTTBoard类的getBoardStr()方法而不是重复代码来完成这项工作。将以下内容添加到您的tictactoe_oop.py文件的末尾 class HintBoard(TTTBoard):def getBoardStr(self):Return a text-representation of the board with hints.boardStr super().getBoardStr() # Call getBoardStr() in TTTBoard. # 1xCanWin FalseoCanWin FalseoriginalSpaces self._spaces # Backup _spaces. # 2for space in ALL_SPACES: # Check each space:# Simulate X moving on this space:self._spaces copy.copy(originalSpaces)if self._spaces[space] BLANK:self._spaces[space] Xif self.isWinner(X):xCanWin True# Simulate O moving on this space:self._spaces copy.copy(originalSpaces) # 3if self._spaces[space] BLANK:self._spaces[space] Oif self.isWinner(O):oCanWin Trueif xCanWin:boardStr \nX can win in one more move.if oCanWin:boardStr \nO can win in one more move.self._spaces originalSpacesreturn boardStr首先super().getBoardStr() 1 运行父TTTBoard类的getBoardStr()内部的代码返回井字棋盘的字符串。我们暂时将这个字符串保存在一个名为boardStr的变量中。使用通过重用TTTBoard类的getBoardStr()创建的棋盘字符串该方法中的其余代码处理提示的生成。然后getBoardStr()方法将xCanWin和oCanWin变量设置为False并将self._spaces字典备份为originalSpaces变量 2 。然后一个for循环在从1到9的所有棋盘空间上循环。在循环内部self._spaces属性被设置为一个originalSpaces字典的副本如果正在循环的当前空格为空则在那里放置一个 X。这模拟了 X 在这个空白空间上的下一步移动。对self.isWinner()的调用将确定这是否是一个赢棋如果是则将xCanWin设置为True。然后对 O 重复这些步骤看看 O 是否能在这个空间上移动 3 获胜。这个方法使用copy模块来复制self._spaces中的字典所以在tictactoe.py的顶部添加下面一行 import copy接下来更改main()中的行使其实例化一个HintBoard对象而不是一个TTTBoard对象 gameBoard HintBoard() # Create a TTT board object.除了对main()的这一行修改之外程序的其余部分和以前完全一样。当您现在运行该程序时输出将如下所示 Welcome to Tic-Tac-Toe! --snip--X| | 1 2 3---| |O 4 5 6---| |X 7 8 9 X can win in one more move. What is Os move? (1-9) 5X| | 1 2 3---|O|O 4 5 6---| |X 7 8 9 O can win in one more move. --snip-- The game is a tie! Thanks for playing!在该方法结束时如果xCanWin或oCanWin是True一个附加的声明消息被添加到boardStr字符串中。最后boardStr又回来了。 不是每个被覆盖的方法都需要使用super()如果一个类的覆盖方法做的事情与父类中被覆盖的方法完全不同就没有必要使用super()调用被覆盖的方法。当一个类有不止一个父方法时,super()函数特别有用这将在本章后面的“多重继承”中解释。 首选组合而非继承 继承对于代码重用来说是一项伟大的技术您可能希望在所有的类中立即开始使用它。但是您可能不总是希望基类和子类如此紧密地耦合。创建多层次的继承不会给你的代码增加组织性反而会增加官僚主义。 虽然您可以对具有“是”关系的类使用继承换句话说当子类是一种父类时但是对具有“有”关系的类使用一种称为组合的技术通常是有利的。组合是一种类设计技术它将对象包含在类中而不是继承那些对象的类。这就是我们在给类添加属性时所做的事情。当使用继承设计你的类时支持组合而不是继承。这就是我们在本章和上一章的所有例子中所做的如下表所述 一个对象“有”一定数量的大帆船、镰刀和克努特硬币。一个对象“有”一组九个空格。一个MiniBoard对象是一个TTTBoard对象所以它也“有”一组九个空格。一个HintBoard对象是一个TTTBoard对象所以它也“有”一组九个空格。 让我们回到上一章的WizCoin类。如果我们创建一个新的WizardCustomer类来代表巫师世界中的顾客这些顾客将携带一定数量的钱我们可以通过WizCoin类来表示这些钱。但是这两个阶级之间没有“是一”的关系一个WizardCustomer对象不是一种WizCoin对象。如果我们使用继承可能会产生一些笨拙的代码 import wizcoinclass WizardCustomer(wizcoin.WizCoin): # 1def __init__(self, name):self.name namesuper().__init__(0, 0, 0)wizard WizardCustomer(Alice) print(f{wizard.name} has {wizard.value()} knuts worth of money.) print(f{wizard.name}\s coins weigh {wizard.weightInGrams()} grams.)在这个例子中WizardCustomer继承了一个WizCoin 1 对象的方法比如value()和weightInGrams()。从技术上来说继承自WizCoin的WizardCustomer可以完成与包含WizCoin对象作为属性的WizardCustomer相同的任务。但是wizard.value()和wizard.weightInGrams()方法的名字有误导性它们似乎会返回巫师的价值和重量而不是巫师硬币的价值和重量。此外如果我们后来想为向导的权重添加一个weightInGrams()方法那么这个方法名已经被占用了。 最好有一个WizCoin对象作为属性因为一个巫师顾客“有”一定数量的巫师硬币 import wizcoinclass WizardCustomer:def __init__(self, name):self.name nameself.purse wizcoin.WizCoin(0, 0, 0) # 1wizard WizardCustomer(Alice) print(f{wizard.name} has {wizard.purse.value()} knuts worth of money.) print(f{wizard.name}\s coins weigh {wizard.purse.weightInGrams()} grams.)我们没有让WizardCustomer类从WizCoin继承方法而是给了WizardCustomer类一个purse属性 1 其中包含一个WizCoin对象。当使用组合时对WizCoin类方法的任何改变都不会改变WizardCustomer类的方法。这种技术为两个类的未来设计变更提供了更大的灵活性并使代码更易于维护。 继承的缺点 继承的主要缺点是将来您对父类所做的任何更改必然会被它的所有子类继承。在大多数情况下这种紧密耦合正是您想要的。但是在某些情况下您的代码需求不太适合您的继承模型。 例如假设我们在一个车辆模拟程序中有Car、Motorcycle和LunarRover类。他们都需要类似的方法比如startIgnition()和changeTire()。我们可以创建一个父类Vehicle并让Car、Motorcycle和LunarRover继承它而不是将这些代码复制并粘贴到每个类中。现在如果我们需要修复一个 bug比如说,changeTire()方法我们只需要在一个地方进行修改。如果我们有几十个继承自Vehicle的不同的车辆相关类这尤其有用。这些类的代码如下所示 class Vehicle:def __init__(self):print(Vehicle created.)def startIgnition(self):pass # Ignition starting code goes here.def changeTire(self):pass # Tire changing code goes here.class Car(Vehicle):def __init__(self):print(Car created.)class Motorcycle(Vehicle):def __init__(self):print(Motorcycle created.)class LunarRover(Vehicle):def __init__(self):print(LunarRover created.)但是将来对Vehicle的所有更改也会影响这些子类。如果我们需要一个changeSparkPlug()方法会怎么样汽车和摩托车有带火花塞的内燃机但月球车没有。通过支持组合胜过继承我们可以创建单独的CombustionEngine和ElectricEngine类。然后我们设计了Vehicle类这样它就“有一个”engine attribute, either a CombustionEngine或ElectricEngine对象使用适当的方法 class CombustionEngine:def __init__(self):print(Combustion engine created.)def changeSparkPlug(self):pass # Spark plug changing code goes here.class ElectricEngine:def __init__(self):print(Electric engine created.)class Vehicle:def __init__(self):print(Vehicle created.)self.engine CombustionEngine() # Use this engine by default. --snip--class LunarRover(Vehicle):def __init__(self):print(LunarRover created.)self.engine ElectricEngine()这可能需要重写大量的代码特别是如果你有几个继承自先前存在的Vehicle类的类对于Vehicle类或其子类的每个对象所有的vehicleObj.changeSparkPlug()调用都需要变成vehicleObj.engine.changeSparkPlug()。因为如此大的变化可能会引入错误所以您可能希望简单地让LunarVehicle的changeSparkPlug()方法什么都不做。在这种情况下Python 风格的方法是在LunarVehicle类内将changeSparkPlug设置为None class LunarRover(Vehicle):changeSparkPlug Nonedef __init__(self):print(LunarRover created.)changeSparkPlug None行遵循本章后面的“类属性”中描述的语法。这覆盖了从Vehicle继承的changeSparkPlug()方法所以用LunarRover对象调用它会导致错误 myVehicle LunarRover() LunarRover created.myVehicle.changeSparkPlug() Traceback (most recent call last):File stdin, line 1, in module TypeError: NoneType object is not callable如果我们试图用一个LunarRover对象调用这个不合适的方法这个错误允许我们快速失败并立即发现问题。任何LunarRover的子类也为changeSparkPlug()继承这个None值。TypeError: NoneType object is not callable错误信息告诉我们LunarRover类的程序员有意将changeSparkPlug()方法设置为None。如果一开始就没有这样的方法我们就会收到一条NameError: name changeSparkPlug is not defined错误消息。 继承可以创造出复杂矛盾的阶级。用作文来代替通常是有利的。 isinstance()和issubclass()函数 当我们需要知道一个对象的类型时我们可以将该对象传递给内置的type()函数如前一章所述。但是如果我们正在做一个对象的类型检查使用更灵活的内置函数isinstance()是一个更好的主意。如果对象属于给定类或给定类的子类则isinstance()函数将返回True。在交互式 Shell 中输入以下内容 class ParentClass: ... pass ...class ChildClass(ParentClass): ... pass ...parent ParentClass() # Create a ParentClass object.child ChildClass() # Create a ChildClass object.isinstance(parent, ParentClass) Trueisinstance(parent, ChildClass) Falseisinstance(child, ChildClass) # 1 Trueisinstance(child, ParentClass) # 2 True注意isinstance()表示child中的ChildClass对象是ChildClass 1 的实例也是ParentClass 2 的实例。这是有意义的因为一个ChildClass对象是一种ParentClass对象。 您还可以传递一个类对象元组作为第二个参数以查看第一个参数是否是元组中的任何一个类 isinstance(42, (int, str, bool)) # True if 42 is an int, str, or bool. True不太常用的issubclass()内置函数可以识别为第一个参数传递的类对象是否是为第二个参数传递的类对象的子类或同一个类: issubclass(ChildClass, ParentClass) # ChildClass subclasses ParentClass. Trueissubclass(ChildClass, str) # ChildClass doesnt subclass str. Falseissubclass(ChildClass, ChildClass) # ChildClass is ChildClass. True就像使用isinstance()一样您可以将一个类对象元组作为第二个参数传递给issubclass()以查看第一个参数是否是元组中任何类的子类。isinstance()和issubclass()的关键区别在于issubclass()被传递了两个类对象而isinstance()被传递了一个对象和一个类对象。 类方法 类方法与类相关联而不是像常规方法那样与单个对象相关联。当您看到两个标记时您可以在代码中识别出一个类方法方法的def语句前的classmethod装饰器和使用cls作为第一个参数如下例所示。 class ExampleClass:def exampleRegularMethod(self):print(This is a regular method.)classmethoddef exampleClassMethod(cls):print(This is a class method.)# Call the class method without instantiating an object: ExampleClass.exampleClassMethod()obj ExampleClass() # Given the above line, these two lines are equivalent: obj.exampleClassMethod() obj.__class__.exampleClassMethod()除了self引用一个对象而cls引用一个对象的类之外cls参数的行为类似于self。这意味着类方法中的代码不能访问单个对象的属性或调用对象的常规方法。类方法只能调用其他类方法或访问类属性。我们使用名称cls是因为class是一个 Python 关键字就像其他关键字一样比如if、while或import我们不能将其用于参数名称。我们经常通过类对象调用类属性如在ExampleClass.exampleClassMethod()中。但是我们也可以通过类的任何对象调用它们就像在obj.exampleClassMethod()中一样。 类方法并不常用。最常见的用例是提供除了__init__()之外的可选构造方法。例如如果构造器既可以接受新对象需要的数据字符串也可以接受包含新对象需要的数据的文件名字符串会怎么样呢我们不希望__init__()方法的参数列表冗长而混乱。相反让我们使用类方法来返回一个新的对象。 例如让我们创建一个AsciiArt类。正如你在第 14 章看到的ASCII 艺术画使用文本字符来形成图像。 class AsciiArt:def __init__(self, characters):self._characters charactersclassmethoddef fromFile(cls, filename):with open(filename) as fileObj:characters fileObj.read()return cls(characters)def display(self):print(self._characters)# Other AsciiArt methods would go here...face1 AsciiArt( _______\n | . . |\n | \\___/ |\n |_______|) face1.display()face2 AsciiArt.fromFile(face.txt) face2.display()AsciiArt类有一个__init__()方法可以将图像的文本字符作为字符串传递给它。它还有一个fromFile()类方法可以传递包含 ASCII 艺术画的文本文件的文件名字符串。这两个方法都创建了AsciiArt对象。 当您运行这个程序并且有一个包含 ASCII 艺术画图面的face.txt文件时输出将看起来像这样 _______ | . . | | \___/ | |_______|_______ | . . | | \___/ | |_______|与让__init__()做所有事情相比fromFile()类方法让你的代码更容易阅读。 类方法的另一个好处是AsciiArt的子类可以继承它的fromFile()方法并在必要时覆盖它。这就是为什么我们在AsciiArt类的fromFile()方法中称cls为(characters)而不是AsciiArt(characters)。cls()调用也可以在AsciiArt的子类中工作无需修改因为AsciiArt类没有被硬编码到方法中。但是一个AsciiArt()调用总是调用AsciiArt类的__init__()而不是子类的__init__()。您可以将cls理解为“表示这个类的对象” 请记住正如常规方法应该总是在代码中的某个地方使用它们的self参数一样类方法应该总是使用它的cls参数。如果你的类方法的代码从不使用cls参数这表明你的类方法可能只是一个函数。 类属性 类属性是属于类而不是对象的变量。我们在类内部但在所有方法外部创建类属性就像我们在.py文件中而在所有函数之外创建全局变量一样。下面是一个名为count的类属性的例子它跟踪已经创建了多少个CreateCounter对象 class CreateCounter:count 0 # This is a class attribute.def __init__(self):CreateCounter.count 1print(Objects created:, CreateCounter.count) # Prints 0. a CreateCounter() b CreateCounter() c CreateCounter() print(Objects created:, CreateCounter.count) # Prints 3.CreateCounter类有一个名为count的类属性。所有的CreateCounter对象共享这个属性而不是有它们自己单独的count属性。这就是为什么构造器中的CreateCounter.count 1行可以记录每个被创建的CreateCounter对象。当您运行该程序时输出将如下所示 Objects created: 0 Objects created: 3我们很少使用类属性。甚至这个“计算已经创建了多少个CreateCounter对象”的例子也可以通过使用一个全局变量而不是一个类属性来更简单地完成。 静态方法 一个静态方法没有一个self或cls参数。静态方法实际上只是函数因为它们不能访问类或其对象的属性或方法。在 Python 中很少需要使用静态方法。如果您决定使用一个函数那么您应该考虑创建一个常规函数。 我们通过在静态方法的def语句之前放置staticmethod装饰器来定义静态方法。下面是一个静态方法的例子。 class ExampleClassWithStaticMethod:staticmethoddef sayHello():print(Hello!)# Note that no object is created, the class name precedes sayHello(): ExampleClassWithStaticMethod.sayHello()在ExampleClassWithStaticMethod类中的sayHello()静态方法和sayHello()函数之间几乎没有区别。事实上您可能更喜欢使用函数因为您可以调用它而不必事先输入类名。 静态方法在没有 Python 灵活语言特性的其他语言中更常见。Python 包含的静态方法模仿了其他语言的特性但没有提供多少实用价值。 何时使用面向对象的类和静态特性 你很少需要类方法、类属性和静态方法。它们也容易被过度使用。如果你在想“为什么我不能用一个函数或者全局变量来代替”这暗示您可能不需要使用类方法、类属性或静态方法。这本中级水平的书介绍它们的唯一原因是当你在代码中遇到它们时你可以认出它们但是我不鼓励你使用它们。如果您正在创建自己的框架其中包含一系列精心设计的类而这些类又会被使用该框架的程序员子类化那么它们会非常有用。但是当您编写简单的 Python 应用时您很可能不需要它们。 关于这些特性以及为什么你需要或不需要它们的更多讨论请阅读 Phillip J. Eby 在dirtsimple.org/2004/12/python-is-not-java.html发表的文章“Python 不是 Java”以及 Ryan Tomayko 在tomayko.com/blog/2004/the-static-method-thing发表的文章“静态方法”。 面向对象的流行语 对 OOP 的解释通常以大量术语开始如继承、封装和多态。知道这些术语的重要性被高估了但你至少应该对它们有一个基本的了解。我已经介绍了继承所以我将在这里描述其他概念。 封装 封装这个词有两个常见但相关的定义。第一个定义是封装是将相关的数据和代码捆绑成一个单元。封装意味着将装箱。这基本上就是类所做的它们组合相关的属性和方法。例如我们的WizCoin类将三个整数knuts、sickles和galleons封装成一个单独的WizCoin对象。 第二个定义是封装是一种信息隐藏技术它让对象隐藏关于对象如何工作的复杂实现细节。你可以在 282 页的“私有属性和私有方法”中看到这一点其中BankAccount对象提供了deposit()和withdraw()方法来隐藏它们的_balance属性如何被处理的细节。函数服务于类似的黑盒目的math.sqrt()函数如何计算一个数的平方根是隐藏的。你只需要知道函数返回你传递给它的数字的平方根。 多态 多态允许一种类型的对象被视为另一种类型的对象。例如len()函数返回传递给它的参数的长度。您可以将一个字符串传递给len()来查看它有多少个字符但是您也可以将一个列表或字典传递给len()来查看它分别有多少个条目或键值对。这种形式的多态被称为通用函数或参数多态因为它可以处理许多不同类型的对象。 多态也指特别多态或操作符重载其中操作符如或*可以根据它们操作的对象类型有不同的行为。例如操作符在处理两个整数值或浮点值时做数学加法但在处理两个字符串时做字符串连接。运算符重载将在第 17 章中介绍。 何时不使用继承 使用继承很容易过度工程化你的类。正如卢西亚诺·拉马尔霍所说“将物品整齐有序地摆放能激发我们的秩序感程序员这么做只是为了好玩。”当一个类或者一个模块中的几个函数可以达到同样的效果时我们将创建类、子类和子类。但是回想一下第 6 章中的 Python 信条简单比复杂好。 使用 OOP 允许你将你的代码组织成更小的单元在这里是类比一个大的py文件包含数百个没有特定顺序定义的函数更容易推理。如果有几个函数都在同一个字典或列表数据结构上操作继承就很有用。在这种情况下将它们组织成一个类是有益的。 但是这里有一些不需要创建类或使用继承的例子 如果你的类由从不使用self或cls参数的方法组成删除类并使用函数代替方法。如果你已经创建了一个只有一个子类的父类但从未创建父类的对象你可以将它们合并成一个类。如果你创建了三或四层以上的子类你可能会不必要地使用继承。将这些子类合并成更少的类。 正如上一章井字棋程序的非面向对象和面向对象版本所展示的那样当然有可能不使用类而仍然有一个工作的、无错误的程序。不要觉得你必须把你的程序设计成一些复杂的类网络。有效的简单解决方案比无效的复杂解决方案要好。乔尔·斯波尔斯基在他的博客文章“不要让宇航员架构师吓到你”中写道。 您应该知道像继承这样的面向对象概念是如何工作的因为它们可以帮助您组织代码并使开发和调试更容易。由于 Python 的灵活性这种语言不仅提供了 OOP 特性而且当它们不适合你的程序需求时也不要求你使用它们。 多重继承 许多编程语言将类限制为最多一个父类。Python 通过提供一个名为多重继承的特性来支持多个父类。例如我们可以有一个带有flyInTheAir()方法的Airplane类和一个带有floatOnWater()方法的Ship类。然后我们可以创建一个继承自Airplane和Ship的FlyingBoat类方法是在class语句中列出两者用逗号分隔。打开一个新的文件编辑器窗口将以下内容另存为flyingboat.py : class Airplane:def flyInTheAir(self):print(Flying...)class Ship:def floatOnWater(self):print(Floating...)class FlyingBoat(Airplane, Ship):pass我们创建的FlyingBoat对象将继承flyInTheAir()和floatOnWater()方法正如您在交互式 Shell 中看到的 from flyingboat import *seaDuck FlyingBoat()seaDuck.flyInTheAir() Flying...seaDuck.floatOnWater() Floating...只要父类的方法名不同且不重叠多重继承就很简单。这些种类的类被称为混合。这只是对一类的统称Python 没有mixin关键字。但是当我们从多个共享方法名的复杂类继承时会发生什么呢 例如考虑本章前面的MiniBoard和HintTTTBoard井字棋棋盘类。如果我们想要一个显示微型井字棋棋盘并提供提示的类呢通过多重继承我们可以重用这些现有的类。将以下内容添加到您的tictactoe_oop.py文件的末尾但在调用main()函数的if语句之前 class HybridBoard(HintBoard, MiniBoard):pass这个类什么都没有。它通过继承HintBoard和MiniBoard来重用代码。接下来更改main()函数中的代码使其创建一个HybridBoard对象 gameBoard HybridBoard() # Create a TTT board object.两个父类MiniBoard和HintBoard都有一个名为getBoardStr()的方法那么HybridBoard继承哪个呢当您运行这个程序时输出会显示一个微型的井字棋棋盘但也会提供提示 --snip--X.. 123.O. 456X.. 789 X can win in one more move.Python 似乎神奇地合并了MiniBoard类的getBoardStr()方法和HintBoard类的getBoardStr()方法来实现这两者但这是因为我已经编写了它们来相互协作。事实上如果你在HybridBoard类的class语句中改变类的顺序看起来就像这样 class HybridBoard(MiniBoard, HintBoard): 你完全失去了暗示 --snip--X.. 123.O. 456X.. 789要理解为什么会这样你需要理解 Python 的方法解析顺序MRO以及super()函数实际上是如何工作的。 方法解析顺序 我们的井字棋程序现在有四个类来代表棋盘三个用定义的getBoardStr()方法一个用继承的getBoardStr()方法如图图 16-2 所示。 图 16-2我们井字棋程序中的四个类 当我们在一个HybridBoard对象上调用getBoardStr()时Python 知道HybridBoard类没有这个名称的方法所以它检查它的父类。但是该类有两个父类它们都有一个getBoardStr()方法。哪个会被叫到 您可以通过检查HybridBoard类的 MRO 来找到答案这是 Python 在继承方法或方法调用super()函数时检查的类的有序列表。通过在交互式 Shell 中调用HybridBoard类的mro()方法可以看到该类的 MRO: from tictactoe_oop import *HybridBoard.mro() [class tictactoe_oop.HybridBoard, class tictactoe_oop.HintBoard, class tictactoe_oop.MiniBoard, class tictactoe_oop.TTTBoard, class object]从这个返回值可以看出当在HybridBoard上调用一个方法时Python 首先在HybridBoard类中检查它。如果不存在Python 检查HintBoard类然后是MiniBoard类最后是TTTBoard类。在每个 MRO 列表的末尾是内置的object类它是 Python 中所有类的父类。 对于单一继承确定 MRO 很容易只需创建一个父类链。对于多重继承就比较棘手了。Python 的 MRO 遵循 C3 算法其细节超出了本书的范围。但是您可以通过记住两条规则来确定 MRO: Python 在检查父类之前检查子类。Python 检查在class语句中从左到右列出的继承类。 如果我们在一个HybridBoard对象上调用getBoardStr()Python 首先检查HybridBoard类。然后因为类的双亲从左到右是HintBoard和MiniBoardPython 检查HintBoard。这个父类有一个getBoardStr()方法所以HybridBoard继承并调用它。 但这并没有结束接下来这个方法调用super().getBoardStr()。super是 Python 的super()函数的一个有点误导的名字因为它不返回父类而是返回 MRO 中的下一个类。这意味着当我们在一个HybridBoard对象上调用getBoardStr()时它的 MRO 中的下一个类在HintBoard之后是MiniBoard而不是父类TTTBoard。所以对super().getBoardStr()的调用调用了MiniBoard类的getBoardStr()方法该方法返回微型井字牌字符串。这个super()调用之后的HintBoard类的getBoardStr()中的剩余代码将提示文本附加到这个字符串中。 如果我们改变HybridBoard类的class语句使其首先列出MiniBoard其次列出HintBoard其 MRO 将把MiniBoard放在HintBoard之前。这意味着HybridBoard从MiniBoard继承getBoardStr()而MiniBoard没有对super()的调用。这种排序导致了微型井字棋棋盘显示无提示的错误没有super()调用MiniBoard类的getBoardStr()方法永远不会调用HintBoard类的getBoardStr()方法。 多重继承允许你用少量的代码创建大量的功能但是容易导致过度工程化难以理解的代码。支持单一继承、混合类或无继承。这些技术通常能够更好地完成程序的任务。 总结 继承是一种代码重用技术。它允许您创建继承其父类方法的子类。您可以覆盖这些方法来为它们提供新的代码但是也可以使用super()函数来调用父类中的原始方法。子类与其父类具有“是”关系因为子类的对象是父类的一种对象。 在 Python 中使用类和继承是可选的。一些程序员认为大量使用继承带来的复杂性不值得。使用组合而不是继承通常更灵活因为它实现了与一个类的对象和其他类的对象的“有”关系而不是直接从那些其他类继承方法。这意味着一个类的对象可以拥有另一个类的对象。例如Customer对象可能有一个被分配给Date对象的birthdate属性而不是被分配给Customer类的子类Date。 正如type()可以返回传递给它的对象的类型一样isinstance()和issubclass()函数返回传递给它们的对象的类型和继承信息。 类可以有对象方法和属性但也可以有类方法、类属性和静态方法。虽然这些很少使用但是它们可以支持全局变量和函数所不能提供的其他面向对象的技术。 Python 允许类从多个父类继承尽管这可能导致代码难以理解。super()函数和一个类的方法决定了如何基于 MRO 继承方法。您可以通过调用类的mro()方法在交互式 Shell 中查看类的 MRO。 本章和上一章涵盖了一般的 OOP 概念。在下一章我们将探索 Python 特有的 OOP 技术。
http://www.dnsts.com.cn/news/41801.html

相关文章:

  • 外贸营销型网站建设门户网站的推广方案
  • 网站首页自动下拉广告wordpress 干什么
  • 启东市住房城乡建设局网站织梦和wordpress哪个安全
  • 北京网站设计联系电话wordpress需要Apache吗
  • 站内推广的几种方式wordpress打开后台为404
  • 北京网站建设公司 fim怎么建立公众号写文章
  • 网站的优化方案南磨房网站建设公司
  • 网站开发的外文翻译二手网站哪些做的比较好
  • 免费网站空间免费主机南京住房和城乡建设部网站
  • 网站构造网络营销10大平台
  • 住房及城乡建设部网站装潢设计是什么
  • 做网站的得多少钱网站支付可以做二清
  • php开发网站建设书籍做网站怎么维护
  • 电商网站获取流量的方法河源新闻头条最新新闻
  • 建交易网站需要多少钱教育网站制作设计
  • 网站基础三要素wordpress栏目管理
  • 广东手机网站建设h5页面制作是什么
  • 上海网站优化彩妆做推广的网站
  • 网站服务器地址查询方法广州白云机场网站建设
  • 网站关键词搜索优化是怎么做的免费网站建设推荐
  • 台州低价网站建设网站建设过程报告
  • 电子商务网站的建设的意义济南广告公司
  • phpstudy配置网站品牌推广途径
  • 男女做暧昧视频网站2023还能上的网站
  • php 可以自己做网站吗莆田外贸专业建站
  • 创业型企业网站模板阳江房产网上半年海怡新
  • 做邀请函的网站搜索引擎优化员简历
  • 龙华网站建设销售员中国海关数据查询平台
  • 大型网站建设价格多少查询百度关键词排名
  • 注塑模具东莞网站建设租电信服务器开网站