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

外贸网站推广平台有哪些门户网站建设公司案例

外贸网站推广平台有哪些,门户网站建设公司案例,怎么注册个人工作室,百度收录网站怎么更改关键词原文#xff1a;http://inventwithpython.com/beyond/chapter15.html OOP 是一种编程语言特性#xff0c;允许你将变量和函数组合成新的数据类型#xff0c;称为类#xff0c;你可以从中创建对象。通过将代码组织成类#xff0c;可以将一个整体程序分解成更容易理解和调试… 原文http://inventwithpython.com/beyond/chapter15.html OOP 是一种编程语言特性允许你将变量和函数组合成新的数据类型称为类你可以从中创建对象。通过将代码组织成类可以将一个整体程序分解成更容易理解和调试的小部分。 对于小程序来说OOP 与其说是增加了组织不如说是增加了官僚主义。虽然有些语言比如 Java要求你将所有代码组织成类但是 Python 的 OOP 特性是可选的。程序员可以在需要时利用类或者在不需要时忽略它们。Python 核心开发人员 Jack Diederich 在 PyCon 2012 的演讲“停止编写类”youtu.be/o9pEzgHorH0中指出在许多情况下程序员编写类时更简单的函数或模块会工作得更好。 也就是说作为一名程序员你应该熟悉什么是类以及它们如何工作的基础知识。在这一章中你将学习什么是类为什么在程序中使用它们以及它们背后的语法和编程概念。OOP 是一个广泛的话题本章只是作为一个介绍。 真实世界的类比填写表单 在您的生活中您很可能已经无数次地填写纸质或电子表单为了看医生、为了网上购物或为了婚礼回复。表单是另一个人或组织收集他们需要的关于您的信息的统一方式。不同的表格要求不同种类的信息。你会在医生的表格上报告一个敏感的医疗状况你会在婚礼回复上报告你带来的任何客人但不是相反。 在 Python 中类、类型、数据类型含义相同。与纸质或电子表单一样类是 Python 对象也称为实例的蓝图其中包含表示名词的数据。这个名词可以是医生的病人、电子商务购物或婚礼宾客。类就像一个空白的表单模板从该类创建的对象就像一个填写好的表单其中包含了表单所代表的事物的实际数据。例如在图 15-1 中RSVP 响应表单就像一个类而填写好的 RSVP 就像一个对象。 图 15-1婚礼 RSVP 表单模板就像类填写好的表单就像对象。 你也可以把类和对象想象成电子表格如图 15-2 所示。 图 15-2所有 RSVP 数据的电子表格 列标题组成了类而每一行组成了一个对象。 在现实世界中类和对象经常被当作项目的数据模型来谈论但是不要把映射和领域混淆了。什么进入类取决于程序需要做什么。图 15-3 显示了一些不同类的对象它们代表了同一个现实世界的人除了这个人的名字它们存储了完全不同的信息。 图 15-3四个对象由不同的类组成代表同一个真实世界的人这取决于软件应用需要了解这个人的什么 另外你的类中包含的信息应该取决于你的程序的需求。许多 OOP 教程使用一个Car类作为它们的基本例子却没有注意到什么进入一个类完全取决于你正在编写的软件的种类。没有一个通用的Car类会明显地有一个honkHorn()方法或者numberOfCupholders属性仅仅因为那些是真实世界的汽车所具有的特征。你的程序可能是一个汽车经销商网络应用一个赛车视频游戏或者一个道路交通模拟。汽车经销商 Web 应用的Car类可能有milesPerGallon或manufacturersSuggestedRetailPrice属性就像汽车经销商的电子表格可能使用这些作为列。但是视频游戏和道路交通模拟没有这些属性因为这些信息与它们无关。视频游戏的Car类可能有一个explodeWithLargeFireball()方法但是汽车经销商和交通模拟但愿不会有。 从类创建对象 您已经在 Python 中使用过类和对象即使您自己没有创建过类。考虑一下datetime模块它包含一个名为date的类。datetime.date类的对象也简称为datetime.date对象或date对象代表一个特定的日期。在交互式 Shell 中输入以下内容创建一个datetime.date类的对象 import datetimebirthday datetime.date(1999, 10, 31) # Pass the year, month, and day.birthday.year 1999birthday.month 10birthday.day 31birthday.weekday() # weekday() is a method; note the parentheses. 6属性是与对象相关联的变量。对datetime.date()的调用创建了一个新的date对象用参数1999、10、31初始化因此该对象表示日期 1999 年 10 月 31 日。我们将这些参数指定为date类的year、month和day属性所有date对象都有这些属性。 有了这些信息类的weekday()方法就可以计算出星期几。在这个例子中它返回周日的6因为根据 Python 的在线文档weekday()的返回值是一个整数从周一的0开始到周日的6。文档列出了date类的对象拥有的其他几个方法。尽管date对象包含多个属性和方法但它仍然是一个可以存储在变量中的对象比如本例中的birthday。 创建一个简单的类WizCoin 让我们创建一个WizCoin类它代表一个虚构的巫师货币中的一些硬币。在这种货币中面额为克努特、西克尔价值 29 克努特和加隆价值 17 西克尔或 493 克努特。请记住WizCoin类中的对象代表一定数量的硬币而不是一定数量的钱。例如它会告诉你你拿的是 5 个 25 美分和 1 个 10 美分而不是 1.35 美元。 在名为wizcoin.py的新文件中输入以下代码创建WizCoin类。注意__init__方法名在init前后有两个下划线我们将在本章后面的“方法、__init__()和self”中讨论__init__ class WizCoin: # 1def __init__(self, galleons, sickles, knuts): # 2Create a new WizCoin object with galleons, sickles, and knuts.self.galleons galleonsself.sickles sicklesself.knuts knuts# NOTE: __init__() methods NEVER have a return statement.def value(self): # 3The value (in knuts) of all the coins in this WizCoin object.return (self.galleons * 17 * 29) (self.sickles * 29) (self.knuts)def weightInGrams(self): # 4Returns the weight of the coins in grams.return (self.galleons * 31.103) (self.sickles * 11.34) (self.knuts * 5.0)这个程序使用一个class语句 1 定义了一个名为WizCoin的新类。创建一个类会创建一个新类型的对象。使用class语句定义一个类类似于使用def语句定义新函数。在class语句后面的代码块中有三个方法的定义__init__()初始化器的缩写 2 、value() 3 和weightInGrams() 4 。请注意所有方法都有一个名为self的第一个参数我们将在下一节探讨这个参数。 按照惯例模块名比如我们的wizcoin.py文件中的wizcoin是小写的而类名比如WizCoin以大写字母开头。不幸的是Python 标准库中的一些类比如date并没有遵循这个约定。 为了练习创建WizCoin类的新对象在一个单独的文件编辑器窗口中输入下面的源代码并将文件保存为wcexample1.py与wizcoin.py放在同一个文件夹中 import wizcoinpurse wizcoin.WizCoin(2, 5, 99) # The ints are passed to __init__(). # 1 print(purse) print(G:, purse.galleons, S:, purse.sickles, K:, purse.knuts) print(Total value:, purse.value()) print(Weight:, purse.weightInGrams(), grams)print()coinJar wizcoin.WizCoin(13, 0, 0) # The ints are passed to __init__(). # 2 print(coinJar) print(G:, coinJar.galleons, S:, coinJar.sickles, K:, coinJar.knuts) print(Total value:, coinJar.value()) print(Weight:, coinJar.weightInGrams(), grams)对WizCoin() 1 2 的调用创建一个WizCoin对象并为它们运行__init__()方法中的代码。我们将三个整数作为参数传递给WizCoin()它们被转发给__init__()的参数。这些参数被分配给对象的self.galleons、self.sickles和self.knuts属性。注意正如time.sleep()函数要求您首先导入time模块并将time.放在函数名之前我们也必须导入wizcoin并将wizcoin.放在WizCoin()函数名之前。 当您运行该程序时输出将类似于以下内容 wizcoin.WizCoin object at 0x000002136F138080 G: 2 S: 5 K: 99 Total value: 1230 Weight: 613.906 gramswizcoin.WizCoin object at 0x000002136F138128 G: 13 S: 0 K: 0 Total value: 6409 Weight: 404.339 grams如果你得到一个错误信息比如ModuleNotFoundError: No module named wizcoin检查以确保你的文件被命名为wizcoin.py并且它和wcexample1.py在同一个文件夹中。 WizCoin对象没有有用的字符串表示所以打印purse和coinJar会在尖括号中显示一个内存地址。你将在第 17 章学习如何改变这一点。 正如我们可以在一个字符串对象上调用lower()字符串方法一样我们也可以在已经分配给purse和coinJar变量的WizCoin对象上调用value()和weightInGrams()方法。这些方法根据对象的galleons、sickles和knuts属性计算值。 类和 OOP 可以产生更多的可维护的代码——也就是说将来更容易阅读、修改和扩展的代码。让我们更详细地探索这个类的方法和属性。 方法、__init__()和self 方法是与特定类的对象相关联的函数。回想一下lower()是一个字符串方法这意味着它是在字符串对象上调用的。你可以在一个字符串上调用lower()就像在Hello.lower()中一样但是你不能在一个列表上调用它比如[dog, cat].lower()。另外注意方法跟在对象后面正确的代码是Hello.lower()而不是lower(Hello)。不像像lower()这样的方法像len()这样的函数不与单一数据类型相关联您可以将字符串、列表、字典和许多其他类型的对象传递给len()。 正如您在上一节中看到的我们通过调用类名作为函数来创建对象。这个函数被称为构造器或者构造器或者缩写为ctor发音为“see-tore”因为它构造了一个新的对象。我们还说构造器实例化了一个新的类实例。 调用构造器会导致 Python 创建新对象然后运行__init__()方法。不要求类有一个__init__()方法但是它们几乎总是有。__init__()方法是您通常设置属性初始值的地方。例如回想一下WizCoin的__init__()方法如下所示 def __init__(self, galleons, sickles, knuts):Create a new WizCoin object with galleons, sickles, and knuts.self.galleons galleonsself.sickles sicklesself.knuts knuts# NOTE: __init__() methods NEVER have a return statement.当wcexample1.py程序调用WizCoin(2, 5, 99)时Python 创建一个新的WizCoin对象然后将三个参数2、5和99传递给一个__init__()调用。但是__init__()方法有四个参数self、galleons、sickles和knuts。原因是所有方法都有一个名为self的第一个参数。当对一个对象调用一个方法时该对象被自动传入用于self参数。其余的参数通常被赋给形参。如果您看到一条错误消息比如TypeError: __init__() takes 3 positional arguments but 4 were given您可能忘记了将self参数添加到方法的def语句中。 你不必命名一个方法的第一个参数self你可以给它起任何名字。但是使用self是惯例选择一个不同的名称会使您的代码对其他 Python 程序员来说可读性更差。当你阅读代码时将self作为第一个参数是区分方法和函数的最快方法。类似地如果你的方法的代码从来不需要使用self参数这表明你的方法可能只是一个函数。 WizCoin(2, 5, 99)的2、5和99参数不会自动分配给新对象的属性为此我们需要__init__()中的三个赋值语句。通常情况下__init__()参数的名称与属性相同但是self.galleons中出现的self表示它是对象的属性而galleons是参数。将构造器的参数存储在对象的属性中是一个类的__init__()方法的常见任务。上一节中的datetime.date()调用执行了类似的任务除了我们传递的三个参数是针对新创建的date对象的year、month和day属性。 您之前已经调用了int()、str()、float()和bool()函数在数据类型之间进行转换例如str(3.1415)基于浮点值3.1415返回字符串值3.1415。之前我们将这些描述为函数但是int、str、float和bool实际上是类而int()、str()、float()和bool()函数是返回新的整数、字符串、浮点和布尔对象的构造器。Python 的风格指南推荐使用大写的驼峰大小写作为类名如WizCoin尽管 Python 的许多内置类并不遵循这一约定。 注意调用WizCoin()构造器会返回新的WizCoin对象但是__init__()方法从来没有一个带有返回值的return语句。添加返回值会导致此错误TypeError: __init__() should return None。 属性 属性是与对象相关的变量。Python 文档将属性描述为“点后的任何名称”例如考虑上一节中的birthday.year表达式。year属性是一个跟在点后面的名称。 每个对象都有自己的属性集。当wcexample1.py程序创建两个WizCoin对象并将它们存储在purse和coinJar变量中时它们的属性值不同。您可以像访问任何变量一样访问和设置这些属性。为了练习设置属性打开一个新的文件编辑器窗口并输入以下代码将其作为wcexample2.py保存在与wizcoin.py文件相同的文件夹中 import wizcoinchange wizcoin.WizCoin(9, 7, 20) print(change.sickles) # Prints 7. change.sickles 10 print(change.sickles) # Prints 17.pile wizcoin.WizCoin(2, 3, 31) print(pile.sickles) # Prints 3. pile.someNewAttribute a new attr # A new attribute is created. print(pile.someNewAttribute)当您运行该程序时输出如下所示 7 17 3 a new attr您可以将对象的属性视为类似于字典的键。您可以读取和修改它们的相关值并为对象分配新属性。从技术上讲方法也被认为是类的属性。 私有属性和私有方法 在 C或 Java 之类的语言中属性可以被标记为具有私有访问这意味着编译器或解释器只允许类的方法内部的代码访问或修改该类的对象的属性。但是在 Python 中这种强制是不存在的。所有的属性和方法都是有效的公共访问:类之外的代码可以访问和修改该类中任何对象的任何属性。 但是私有访问是有用的。例如BankAccount类的对象可以有一个balance属性只有BankAccount类的方法可以访问这个属性。出于这些原因Python 的惯例是以单下划线开始私有属性或方法名。从技术上讲没有什么可以阻止类外的代码访问私有属性和方法但是最好的做法是只让类的方法访问它们。 打开一个新的文件编辑器窗口输入以下代码保存为privateExample.py。其中BankAccount类的对象有私有的_name和_balance属性只有deposit()和withdraw()方法可以直接访问 class BankAccount:def __init__(self, accountHolder):# BankAccount methods can access self._balance, but code outside of# this class should not:self._balance 0 # 1self._name accountHolder # 2with open(self._name Ledger.txt, w) as ledgerFile:ledgerFile.write(Balance is 0\n)def deposit(self, amount):if amount 0: # 3return # Dont allow negative deposits.self._balance amountwith open(self._name Ledger.txt, a) as ledgerFile: # 4ledgerFile.write(Deposit str(amount) \n)ledgerFile.write(Balance is str(self._balance) \n)def withdraw(self, amount):if self._balance amount or amount 0: # 5return # Not enough in account, or withdraw is negative.self._balance - amountwith open(self._name Ledger.txt, a) as ledgerFile: # 6ledgerFile.write(Withdraw str(amount) \n)ledgerFile.write(Balance is str(self._balance) \n)acct BankAccount(Alice) # We create an account for Alice. acct.deposit(120) # _balance can be affected through deposit() acct.withdraw(40) # _balance can be affected through withdraw()# Changing _name or _balance outside of BankAccount is impolite, but allowed: acct._balance 1000000000 # 7 acct.withdraw(1000)acct._name Bob # Now were modifying Bobs account ledger! # 8 acct.withdraw(1000) # This withdrawal is recorded in BobLedger.txt!当你运行privateExample.py时它创建的账本文件不准确因为我们在类外修改了_balance和_name导致状态无效。AliceLedger.txt莫名其妙有一大笔钱在里面 Balance is 0 Deposit 120 Balance is 120 Withdraw 40 Balance is 80 Withdraw 1000 Balance is 999999000现在有一个BobLedger.txt文件有一个令人费解的帐户余额尽管我们从未为 Bob 创建过一个BankAccount对象 Withdraw 1000 Balance is 999998000设计良好的类大多是自包含的提供了将属性调整为有效值的方法。_balance和_name属性被标记为私有的 12调整BankAccount类的值的唯一有效方式是通过deposit()和withdraw()方法。这两个方法都有检查 35 来确保_balance没有进入无效状态比如负整数值。这些方法还记录每笔交易的账户当前余额 46。 *修改这些属性的类之外的代码如acct._balance 1000000000 7 或acct._name Bob 8 指令会将对象置于无效状态并引入 bug以及来自银行审查员的审计。通过遵循私有访问的下划线前缀约定可以使调试更加容易。原因是你知道错误的原因会在类的代码中而不是在整个程序的任何地方。 注意与 Java 和其他语言不同Python 不需要私有属性的公共获取器和设置器方法。相反Python 使用属性正如在第 17 章中所解释的。 type()函数和__qualname__属性 将一个对象传递给内置的type()函数通过它的返回值告诉我们对象的数据类型。从type()函数返回的对象是类型对象也称为类对象。回想一下术语类型、数据类型和类在 Python 中都有相同的含义。要查看type()函数针对不同的值返回什么请在交互式 Shell 中输入以下内容 type(42) # The object 42 has a type of int. class intint # int is a type object for the integer data type. class inttype(42) int # Type check 42 to see if it is an integer. Truetype(Hello) int # Type check Hello against int. Falseimport wizcointype(42) wizcoin.WizCoin # Type check 42 against WizCoin. Falsepurse wizcoin.WizCoin(2, 5, 10)type(purse) wizcoin.WizCoin # Type check purse against WizCoin. True注意int是一个类型对象与type(42)返回的是同一类对象但也可以作为int()构造器调用int(42)函数不转换42字符串参数相反它根据参数返回一个整数对象。 假设您需要记录一些关于程序中变量的信息以帮助您稍后调试它们。您只能将字符串写入日志文件但是将类型对象传递给str()将会返回一个看起来相当混乱的字符串。相反使用所有类型对象都有的__qualname__属性来编写一个更简单、人类可读的字符串 str(type(42)) # Passing the type object to str() returns a messy string. class inttype(42).__qualname__ # The __qualname__ attribute is nicer looking. int__qualname__属性最常用于覆盖__repr__()方法这将在第 17 章详细解释。 非面向对象与面向对象的例子井字棋 起初很难理解如何在程序中使用类。让我们看一个不使用类的简短井字棋程序的例子然后重写它使它使用类。 打开一个新的文件编辑器窗口进入以下程序然后保存为tictactoe.py : # tictactoe.py, A non-OOP tic-tac-toe game.ALL_SPACES list(123456789) # The keys for a TTT board dictionary. X, O, BLANK X, O, # Constants for string values.def main():Runs a game of tic-tac-toe.print(Welcome to tic-tac-toe!)gameBoard getBlankBoard() # Create a TTT board dictionary.currentPlayer, nextPlayer X, O # X goes first, O goes next.while True:print(getBoardStr(gameBoard)) # Display the board on the screen.# Keep asking the player until they enter a number 1-9:move Nonewhile not isValidSpace(gameBoard, move):print(fWhat is {currentPlayer}\s move? (1-9))move input()updateBoard(gameBoard, move, currentPlayer) # Make the move.# Check if the game is over:if isWinner(gameBoard, currentPlayer): # First check for victory.print(getBoardStr(gameBoard))print(currentPlayer has won the game!)breakelif isBoardFull(gameBoard): # Next check for a tie.print(getBoardStr(gameBoard))print(The game is a tie!)breakcurrentPlayer, nextPlayer nextPlayer, currentPlayer # Swap turns.print(Thanks for playing!)def getBlankBoard():Create a new, blank tic-tac-toe board.board {} # The board is represented as a Python dictionary.for space in ALL_SPACES:board[space] BLANK # All spaces start as blank.return boarddef getBoardStr(board):Return a text-representation of the board.return f{board[1]}|{board[2]}|{board[3]} 1 2 3---{board[4]}|{board[5]}|{board[6]} 4 5 6---{board[7]}|{board[8]}|{board[9]} 7 8 9def isValidSpace(board, space):Returns True if the space on the board is a valid space numberand the space is blank.return space in ALL_SPACES or board[space] BLANKdef isWinner(board, player):Return True if player is a winner on this TTTBoard.b, p board, player # Shorter names as syntactic sugar.# Check for 3 marks across the 3 rows, 3 columns, and 2 diagonals.return ((b[1] b[2] b[3] p) or # Across the top(b[4] b[5] b[6] p) or # Across the middle(b[7] b[8] b[9] p) or # Across the bottom(b[1] b[4] b[7] p) or # Down the left(b[2] b[5] b[8] p) or # Down the middle(b[3] b[6] b[9] p) or # Down the right(b[3] b[5] b[7] p) or # Diagonal(b[1] b[5] b[9] p)) # Diagonaldef isBoardFull(board):Return True if every space on the board has been taken.for space in ALL_SPACES:if board[space] BLANK:return False # If a single space is blank, return False.return True # No spaces are blank, so return True.def updateBoard(board, space, mark):Sets the space on the board to mark.board[space] markif __name__ __main__:main() # Call main() if this module is run, but not when imported.当您运行该程序时输出将类似于以下内容 Welcome to tic-tac-toe!| | 1 2 3---| | 4 5 6---| | 7 8 9 What is Xs move? (1-9) 1X| | 1 2 3---| | 4 5 6---| | 7 8 9 What is Os move? (1-9) --snip--X| |O 1 2 3---|O| 4 5 6---X|O|X 7 8 9 What is Xs move? (1-9) 4X| |O 1 2 3---X|O| 4 5 6---X|O|X 7 8 9 X has won the game! Thanks for playing!简而言之这个程序使用字典对象来表示井字棋棋盘上的九个空格。字典的键是字符串1到9它的值是字符串X、O或 。数字空间的排列方式与手机键盘相同。 tictactoe . py中的函数执行以下操作 main()函数包含创建新棋盘数据结构的代码存储在gameBoard变量中并调用程序中的其他函数。getBlankBoard()函数返回一个字典其中九个空格设置为空白板的 。getBoardStr()函数接受表示棋盘的字典并返回棋盘的多行字符串表示可以打印到屏幕上。这就是游戏显示的井字棋棋盘文本。如果传递了一个有效的空格数并且该空格为空则isValidSpace()函数返回True。isWinner()函数的参数接受一个棋盘字典和X或O来确定该玩家是否在棋盘上有连续三个标记。isBoardFull()函数决定棋盘上是否没有空格意味着游戏已经结束。updateBoard()函数的参数接受棋盘字典、空格和玩家的 X 或 O 标记并更新字典。 注意许多函数接受变量board作为它们的第一个参数。这意味着这些函数是相互关联的因为它们都在一个公共的数据结构上操作。 当代码中的几个函数都在同一个数据结构上操作时通常最好将它们作为一个类的方法和属性组合在一起。让我们在tictactoe.py程序中对此进行重新设计使用一个TTTBoard类将board字典存储在一个名为spaces的属性中。将board作为参数的函数将成为我们的TTTBoard类的方法并使用self参数而不是board参数。 打开一个新的文件编辑器窗口输入以下代码保存为tictactoe_oop.py : # tictactoe_oop.py, an object-oriented tic-tac-toe game.ALL_SPACES list(123456789) # The keys for a TTT board. X, O, BLANK X, O, # Constants for string values.def main():Runs a game of tic-tac-toe.print(Welcome to tic-tac-toe!)gameBoard TTTBoard() # Create a TTT board object.currentPlayer, nextPlayer X, O # X goes first, O goes next.while True:print(gameBoard.getBoardStr()) # Display the board on the screen.# Keep asking the player until they enter a number 1-9:move Nonewhile not gameBoard.isValidSpace(move):print(fWhat is {currentPlayer}\s move? (1-9))move input()gameBoard.updateBoard(move, currentPlayer) # Make the move.# Check if the game is over:if gameBoard.isWinner(currentPlayer): # First check for victory.print(gameBoard.getBoardStr())print(currentPlayer has won the game!)breakelif gameBoard.isBoardFull(): # Next check for a tie.print(gameBoard.getBoardStr())print(The game is a tie!)breakcurrentPlayer, nextPlayer nextPlayer, currentPlayer # Swap turns.print(Thanks for playing!)class TTTBoard:def __init__(self, usePrettyBoardFalse, useLoggingFalse):Create a new, blank tic tac toe board.self._spaces {} # The board is represented as a Python dictionary.for space in ALL_SPACES:self._spaces[space] BLANK # All spaces start as blank.def getBoardStr(self):Return a text-representation of the board.return f{self._spaces[1]}|{self._spaces[2]}|{self._spaces[3]} 1 2 3---{self._spaces[4]}|{self._spaces[5]}|{self._spaces[6]} 4 5 6---{self._spaces[7]}|{self._spaces[8]}|{self._spaces[9]} 7 8 9def isValidSpace(self, space):Returns True if the space on the board is a valid space numberand the space is blank.return space in ALL_SPACES and self._spaces[space] BLANKdef isWinner(self, player):Return True if player is a winner on this TTTBoard.s, p self._spaces, player # Shorter names as syntactic sugar.# Check for 3 marks across the 3 rows, 3 columns, and 2 diagonals.return ((s[1] s[2] s[3] p) or # Across the top(s[4] s[5] s[6] p) or # Across the middle(s[7] s[8] s[9] p) or # Across the bottom(s[1] s[4] s[7] p) or # Down the left(s[2] s[5] s[8] p) or # Down the middle(s[3] s[6] s[9] p) or # Down the right(s[3] s[5] s[7] p) or # Diagonal(s[1] s[5] s[9] p)) # Diagonaldef isBoardFull(self):Return True if every space on the board has been taken.for space in ALL_SPACES:if self._spaces[space] BLANK:return False # If a single space is blank, return False.return True # No spaces are blank, so return True.def updateBoard(self, space, player):Sets the space on the board to player.self._spaces[space] playerif __name__ __main__:main() # Call main() if this module is run, but not when imported.在功能上这个程序和非 OOP 的井字棋程序是一样的。输出看起来完全相同。我们已经将原来在getBlankBoard()中的代码移到了TTTBoard类的__init__()方法中因为它们执行相同的任务准备棋盘数据结构。我们将其他函数转换成方法用self参数代替旧的board参数因为它们也有相似的用途它们都是在井字棋棋盘数据结构上操作的代码块。 当这些方法中的代码需要改变存储在_spaces属性中的字典时代码使用self._spaces。当这些方法中的代码需要调用其他方法时这些调用的前面也会加上self和一个句号。这类似于《创建一个简单的类WizCoin》中的coinJars.values()在coinJars变量中有一个对象。在这个例子中要调用方法的对象在一个self变量中。 另外请注意,_spaces属性以下划线开头这意味着只有TTTBoard方法内部的代码才能访问或修改它。类外的代码应该只能通过调用修改_spaces的方法来间接修改它。 比较两个井字棋程序的源代码会有所帮助。你可以比较这本书里的代码或者在autbor.com/compareoop查看并列比较。 井字棋是一个小程序不需要太多的努力就能理解。但是如果这个程序有数万行代码包含数百个不同的函数会怎么样呢一个有几十个类的程序比一个有几百个不同函数的程序更容易理解。OOP 将一个复杂的程序分解成更容易理解的程序块。 为现实世界设计类是困难的 设计一个类就像设计一个纸质表单一样看似简单。形式和类本质上是它们所代表的现实世界对象的简化。问题是我们应该如何简化这些对象例如如果我们正在创建一个Customer类客户应该有一个firstName和lastName属性对吗但是实际上创建类来模拟现实世界的对象可能会很棘手。在大多数西方国家一个人的姓是他们的姓但在中国姓是第一位的。如果我们不想排除十几亿的潜在客户我们应该如何改变我们的Customer阶层是不是应该把firstName和lastName改成givenName和familyName但是有些文化不用姓。例如前联合国秘书长吴丹是缅甸人他没有姓Thant 是他的名U 是他父亲名的首字母。我们可能想要记录客户的年龄但是一个age属性很快就会过时相反最好在每次需要时使用birthdate属性计算年龄。 现实世界是复杂的设计表单和类来在我们的程序可以运行的统一结构中捕捉这种复杂性是困难的。电话号码格式因国家而异。邮政编码不适用于美国以外的地址。对于德国小村庄 Schmedeswurtherwesterdeich 来说设定城市名称的最大字符数可能是个问题。在澳大利亚和新西兰你的法定性别可以是 x。鸭嘴兽是一种产卵的哺乳动物。花生不是坚果。热狗可能是三明治也可能不是取决于你问谁。作为一名编写用于现实世界的程序的程序员你必须驾驭这种复杂性。 要了解更多关于这个主题的信息我推荐 Carina C. Zona 在 PyCon 2015 的演讲“真实世界的模式”youtu.be/PYYfVqtcWQY和 North Bay 在 Python 2018 的演讲“嗨我的名字是…”youtu.be/NIebelIpdYk。也有流行的“程序员相信的错误”博客帖子比如“程序员相信的关于名字的错误”和“程序员相信的关于时区的错误”这些博客文章还涵盖了映射、电子邮件地址等主题以及许多程序员经常表现不佳的数据。你可以在github/kdeldycke/awesome-falsehood找到这些文章的链接。此外你会在 CGP Grey 的视频中找到一个捕捉现实世界复杂性的糟糕执行方法的好例子“社会保障卡解释”。 总结 OOP 对于组织你的代码是一个有用的特性。类允许您将数据和代码组合成新的数据类型。您还可以通过调用这些类的构造器作为函数调用的类名从这些类中创建对象然后调用类的__init__()方法。方法是与对象相关联的函数属性是与对象相关联的变量。所有方法都有一个self参数作为它们的第一个参数这个参数在方法被调用时被分配给对象。这允许方法读取或设置对象的属性并调用其方法。 尽管 Python 不允许为属性指定私有或公共访问但它确实有一个惯例即对任何方法或属性使用下划线前缀这些方法或属性只能从类自己的方法中调用或访问。通过遵循这个约定您可以避免误用类并将其设置为可能导致 bug 的无效状态。调用type(obj)将返回obj类型的类对象。类对象有一个__qualname___属性该属性包含一个字符串该字符串具有人类可读形式的类名。 此时您可能会想当我们可以用函数完成同样的任务时为什么还要麻烦地使用类、属性和方法呢OOP 是一种将代码组织成不仅仅是一个py文件里面有 100 个函数的有用方法。通过将你的程序分成几个设计良好的类你可以分别关注每个类。 OOP 是一种关注数据结构和处理这些数据结构的方法的方法。这种方法并不是每个程序都必须使用的当然也有可能过度使用 OOP。但是 OOP 提供了使用许多高级特性的机会我们将在接下来的两章中探讨这些特性。第一个特征是继承我们将在下一章深入探讨。
http://www.w-s-a.com/news/838079/

相关文章:

  • 网站建设与管理网络推广的优点
  • 美食网站的设计与制作做网站的电销话术
  • 中国档案网站建设现状研究陕西建设厅执业资格注册中心网站
  • 网站建设的内容管理怎么用ps切片在dw里做网站
  • 建设婚恋网站用什么搭建涿州网站开发
  • 做知识内容的网站与app哈尔滨哪里有做网站的
  • 青岛企业网站建站模板百度网站建设推广
  • 做360网站中保存的图片存在哪里个人建立网站要多少钱
  • 网站安装部署无锡做网站的公司
  • 怎么将网站做成小程序安装wordpress到服务器
  • 企业网站建设的四大因素沈阳网站建设招标公司
  • wordpress仿站开发公司网站策划宣传
  • 金乡县网站开发网站开发三个流程
  • qq空间网站是多少纺织网站建设方案
  • 建设微网站项目报告网站优化难吗
  • 做网站需要自己上传产品吗企业网站系统设计
  • wordpress个人中心济南网站建设和优化
  • 网站pc端网址和手机端网址建设牡丹江 网站建设
  • 苏州新区城乡建设网站人才招聘网站开发
  • 一般网站是怎么做的威远移动网站建设
  • 赣州网站开发公司怎么才能设计好一个网站
  • 个人网站建设分几个步走培训网站开发哪个好
  • 智能网站价格河北城乡建设网站
  • 做动画在线观看网站网上花店 网站源代码
  • 做网站项目体会商业信息
  • 深圳的设计网站谷歌浏览器下载手机版官网
  • 苏州网站建设都找全网天下外贸响应式网站设计
  • 揭阳专业做网站网站迁移教材
  • 手机上怎么上传网站吗工程信息网站建设
  • 用手机建网站微信手机网站流程