对象
对象是被称为面向对象编程(简称OOP)的庞大编程范式的构建块。类是python的主要面向对象编程工具,因此我们将在python的基础上研究对象类
。类是用来创建和管理新对象和支持的继承——OOP领域的关键组成部分和重用代码的机制。
历史上的注意
对象的思想第一次出现在SIMULA编程语言中,当时它的设计者Ole-Johan Dahl和Kristen Nygaard认为类、对象、继承和动态绑定的概念是设计离散事件模拟程序的简洁方法。
值得注意的是,使用OOP完全是可选的,大多数程序都可以不使用它来编写。你可以通过使用函数和变量做很多事情。使用OOP的主要优点是它是一种非常有用的组织代码的方式,特别是对于非常大的项目。因为使用职业需要一些事先的计划,所以通常情况下,在战略模式下工作的人比在战术模式下工作的人更感兴趣。
类
在面向对象编程中,类是一种可扩展的程序代码模板,用于创建对象,为状态(成员变量)和行为(成员函数、方法)的实现提供初始值。属性创建用户定义的对象类
关键字。类是定义未来对象性质的蓝图。从类中构造实例。实例是由特定类创建的特定对象。
下面是一个简单的python类的例子,它定义了brilliant社区中的人:
1 2 3 4 5 6 7 8 9 10 |
|
以上是一个类
语句定义了一个新类,就像def
语句定义一个新函数。这个类的名字是才华横溢的
。
实例是由特定类创建的特定对象。要创建类的实例,使用类名调用类,并传入类的参数__init__
方法接受。
1 |
|
的新实例才华横溢的
类。换句话说,我们实例化才华横溢的
类
1 |
|
上面定义了方法为才华横溢的
类。方法用于属于类的函数。
这个名字__init__ ()
用于类的“构造函数”。虽然类是新数据类型的蓝图,但您仍然需要创建该数据类型的值,以便拥有可以存储在变量中或传递给函数的内容。
调用时,构造函数创建新对象,在构造函数中运行代码,并返回新对象。这就是user =brilliant ('Mursalin', 17,4)
线。无论类名是什么,构造函数总是被命名__init__
。
另一个常用术语是属性。属性是对象的特征。这个方法叫做__init__ ()
用于初始化对象的属性。就像方法是定义在类中的函数一样,属性是定义在类中的变量。
类定义中的每个方法都以实例对象的引用开始。按照惯例,它被命名为“self”。
在Python中,方法的第一个参数自我
。的自我
参数用于创建成员变量。
构造函数的主体是:
1 2 3 |
|
这看起来有点重复,但这段代码所做的是为构造函数创建的对象创建成员变量。成员变量将以自我
。以显示它们是属于对象的成员变量,而不仅仅是方法中的常规局部变量。
数据
Data指的是属性,这些属性只是我们定义的类的变量。它们也被称为类变量。它们可以像任何其他变量一样使用,因为它们是在创建类时设置的,并且可以通过类中的方法或程序主要部分的其他地方进行更新。Java程序员将这类变量称为静态
。
它们也可以被设置为类的唯一属性。这些类型的类数据被称为实例变量。这意味着它们与类相关联,它们被Java程序员称为非静态变量。
使用静态类数据属性的示例
1 2 3 4 5 6 7>>>类酒吧(对象):…喷火=2>>>打印酒吧。喷火2>>>酒吧。喷火=酒吧。喷火+1>>>打印酒吧。喷火101
类是数据结构定义类型,而实例是该类型变量的声明。实例本质上是赋予生命的类。一个类
声明基本上描述了应该存在的框架。如果任何房屋的单个一般建筑计划是一个类,那么实例将是实际的特定类型的房屋,具有自己的属性,但仍然遵循一般结构。尽管许多其他OOP语言为创建实例类提供了新的关键字,但python只需要使用参数调用类即可。
创建类实例的示例
1 2 3 4 5 6 7 8 9类房子(对象):颜色=没有一个大小=没有一个bluehouse=房子()bluehouse。颜色=“蓝”redhouse=房子()redhouse。颜色=“红色”打印bluehouse。颜色#打印“蓝”打印redhouse。颜色#打印“红色”
方法
方法相当于面向对象世界中的函数。如果您有过程编程背景,也就是说,您已经在程序中使用过函数,那么您就知道调用方法时应该产生一些输出。方法也可以接受参数并操作这些参数,然后根据这些参数生成输出,就像函数一样。但是,为什么不直接将它们称为“函数”呢?“方法”一词的使用并不局限于某种花哨的术语,因为方法并不“仅仅”用于执行简单的计算,而是根据它们在类设计中的目的进行分类。
接口方法
接口方法的目的是提供对象与外部环境的接口(可能是其他对象的方法、来自用户的数据输入、来自另一个对象的数据、不在同一对象内部的任何东西)。OO设计的原则之一是封装:将对象构建为包含其自身所有数据和方法的胶囊。然而,正如您可以猜到的那样,一个与所有事物隔离的对象是毫无用处的,它应该是一个更大系统的一部分。这时就需要接口方法了:它们为对象提供了获取外部输入和输出所需的最小接口,因此它确实可以成为更大系统的一部分,同时本身也是一个更小的系统。
例如,为属性定义getter和setter方法是一种非常好的OO实践,这主要是出于安全和代码完整性的考虑。getter和setter是通过间接方式为外部代理提供内部属性访问的方法(在下面的“对象变量类型和作用域”一节中,您将看到这种方法有用性的示例)。让我们看看它们是如何实现的:
Java中的Getter和Setter接口方法:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40//如果你不知道为什么这里使用keywork 'public',它的目的将在下面的章节“对象变量类型和范围”中明确。公共类广场{// 'double'是属性的数据类型,必须声明。私人双sideLength;// 'double'是方法返回的数据类型,必须声明。/ /获取:公共双getSideLength(){返回这。sideLength;}// 'void'用于表示该方法不返回任何值。参数数据类型必须声明。/ /赋值:公共无效setSideLength(双sideLength){这。sideLength=sideLength;}公共广场(双sideLength){这。sideLength=sideLength;}公共静态无效主要(字符串[]arg游戏){//保存对象的变量的“数据类型”是该对象的类。广场广场=新广场(10.0);系统。出。println(广场。getSideLength());//输出10.0到屏幕广场。setSideLength(3.0);系统。出。println(广场。getSideLength());//打印3.0到屏幕上}}
记住Main()方法是Java标准;它的内容是运行Java程序时将要执行的代码。
Python中的Getter和Setter接口方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20类广场(对象):def__init__(自我,sideLength):自我。sideLength=sideLength# Getter:defgetSideLength(自我):返回自我。sideLength# Setter:defsetSideLength(自我,sideLength):自我。sideLength=sideLength广场=广场(10.0)打印广场。getSideLength()#将10.0打印到屏幕上广场。setSideLength(3.0)打印广场。getSideLength()#打印3.0到屏幕上
这两个代码片段都做了完全相同的事情:定义一个Square类,它将具有通过getter方法'getSideLength'访问的sideLength属性的实例,它将提供该属性的值,并通过setter方法'setSideLength'提供改变该属性值的方法。然后,创建'Square'类的实例,sideLength的初始值为10.0,并将该实例分配给'Square'变量。然后sideLength变量的值被打印到屏幕上,然后sideLength的值被更新,最后sideLength变量的更新值被打印到屏幕上。
如果您对面向对象编程完全不熟悉,可能会使用'Square()' Java方法和初始化() Python方法的含义对你来说是无关紧要的。那是因为它们是另一种方法,a构造函数。
构造函数方法
构造函数方法用于实例化类,这是有效地创建对象的方法(请记住,类是对象的形式,对象是类的实例)。它们通常要么以类命名(如Java和c#),要么以关键字命名(如Python和Ruby)。构造函数方法的参数通常是分配给该对象的部分或全部属性的初始值。您已经在上面的方框中看到了一个构造函数方法分配属性的示例。让我们看一个构造函数的例子,它不接受任何形参,而是将对象属性初始化为一些默认值:
Python中的构造函数方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19类广场(对象):构造函数:def__init__(自我):自我。sideLength=5.0defgetSideLength(自我):返回自我。sideLengthdefsetSideLength(自我,sideLength):自我。sideLength=sideLength广场=广场()//调用的构造函数方法打印广场。getSideLength()#将5.0打印到屏幕上广场。setSideLength(3.0)打印广场。getSideLength()#打印3.0到屏幕上
程序的结构基本相同,除了构造函数方法现在不接受参数,并在为没有单独设置其值的属性调用getter时返回默认值。正如您所看到的,属性的默认值并不意味着该属性对于对象是静态的(稍后将更深入地解释这个Java keywork),只是您已经决定“我想我将稍后定义这个属性,但我希望我的实例现在完全工作”。还要注意,不仅构造函数实现需要遵守一些特定于语言的约定,构造函数调用也需要遵守。在Java中,构造函数方法是通过在调用构造函数(带参数或不带参数)之前添加键工作'new'来调用的。在Python中,构造函数是通过向类传递参数来调用的(这实际上会触发初始化方法)。
实现方法
实现方法是最类似于标准过程函数的方法。顾名思义,这是一个方法,您将在其中实际实现对象的功能。这通常是通过操作对象的属性并提供一些输出来完成的(或者以特定的方式更改一些内部属性,而不提供任何即时输出,这是无限制的)。让我们在out Square对象中实现'outputArea'功能:
Python中的实现方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21类广场(对象):构造函数:def__init__(自我):自我。sideLength=5.0defgetSideLength(自我):返回自我。sideLengthdefsetSideLength(自我,sideLength):自我。sideLength=sideLength#实现功能:defoutputArea():返回自我。getSideLength()*自我。getSideLength()广场=广场()广场。setSideLength(3.0)打印广场。outputArea()#将9.0打印到屏幕上
那么,这里发生了什么?'outputArea()'方法的输出显示边长等于该正方形的sideLength属性的正方形的面积。对象内部方法的基本准则是在其数据内部操作以提供实际功能。任何具有更通用功能而不局限于单个类(或类层次结构)的方法通常都在模块内定义(可以将模块视为旨在提供可应用于许多不同类的功能的方法的集合)。如果Square的sideLength属性是4.0而不是3.0,那么输出将是16.0而不是9.0。该方法也不局限于对其属性执行一些封闭操作,我们可以使用对象的属性和一些任意参数来操作输出,如下所示:
Python中outputArea方法的新实现:
1 2 3 4defoutputAreaTimesN(n):返回(自我。getSideLength()*自我。getSideLength())*n打印广场。outputAreaTimesN(2)#将18.0打印到屏幕上
对象变量类型和范围
在面向对象编程中,方法和变量具有不同的作用域。作用域意味着其他对象或类可以直接访问方法或变量,也可以不直接访问。系统可以访问没有实例的类。
类作用域:类变量和类方法与类相关联。类(对象)的实例并不需要使用这些变量或metnod。类方法不能访问实例变量或方法,只能访问类变量和方法。
实例范围:实例变量和实例方法与特定对象相关联。它们可以访问类变量和方法。
私人空间:私有变量和私有方法只能被它们所在的对象访问。因此,如果出现问题,通常只有一个源文件可以查看。如果你的项目中有一百万行代码,但是你的类保持的很小,这可以大大减少你的错误跟踪工作。
公共空间:公共变量和公共方法可以在包含它们的对象之外访问,这对于实际的考虑意味着“可能在任何地方”。如果公共字段出了问题,罪魁祸首可能在任何地方,因此为了追踪错误,您可能必须查看相当多的代码
保护范围:受保护的变量和受保护的方法可以被它们所在的类和继承的类(子类)访问。
封装:提供一个公共接口与对象交互,同时将其他信息隐藏在对象内部的过程。封装意味着对象的内部表示通常隐藏在对象定义之外的视图中。的米ain way that encapsulation helps reduce rippling effects of change is by keeping as many of the implementation details private to the class. By limiting the interface only to those members needed to use the class, many changes can be made to the implementation without affecting any code that uses the class.
继承
不同种类的物体之间往往有一定的共同之处。例如,优秀的员工和用户都有相同的特征(姓名、年龄、级别)。然而,它们还定义了使它们不同的附加特性:for工作人员
我们可能想要增加薪水、专业领域等等。
面向对象编程允许类从其他类继承常用的状态和行为。在本例中,工作人员现在成为用户的超类。
1 2 3 4 5 |
|
这实际上是在说,“A工作人员
和a一样吗用户
,使用一些额外的方法和成员变量
继承的重要好处是代码重用和降低程序的复杂性。派生类(后代)覆盖或扩展基类(祖先)的功能。
在使用继承时应该小心,因为它可能被滥用。我们必须确定,任何可能的变化或更新,您对用户
类也是您想要的工作人员
类的所有其他子类用户
也有。
继承描述派生类如何继承基本用例的属性。子类继承任何基类的属性,无论它们是数据属性还是方法。
下面是一个例子。父
是一个没有属性的简单类。孩子
派生的类是父
And因此是一个子类。
1 2 3 4 5类父(对象):#父类通过类孩子(父):#子类通过
1 2 3 4 5 |
|
让我们在里面创建一个方法父
将在它的子类中重写:
1 2 3 4 5 6 |
|
现在让我们创建子类
1 2 3 4 5 6 |
|
虽然孩子
继承的父
的有趣的
方法时,它会被覆盖,因为孩子
定义自己的有趣的
方法。重写方法的一个原因是为子类添加特殊的惟一功能。
如果你想调用你在子类中覆盖的基类方法,你可以调用一个未绑定的基类方法,显式地提供子类的实例:
1 2 |
|
模块
为了避免一些繁琐的工作,经常需要使用模块。模块是一个不同的东西,它可能有一个或二十几个密切相关的类。关键在于模块是你要导入的东西,你需要导入的模块对那些阅读、维护和扩展你的软件的人来说是完全合理的。
方法导入模块进口
声明。
1 |
|
在Python中,每个以.py结尾的文件都是一个模块
1 2 3 4 5 6 7 8 |
|
我们将这个文件保存为basic.py
1 2 3 4 5 6 7 |
|
我们将这个文件保存为us more.py。
1 2 3 4 5 6 7 8 9 10 |
|
它会输出
1 2 3 4 5 6 |
|
在上面的例子中,模块basic.py和more.py各自只包含一个类,但这是不需要的。将导入看作是将代码组织成概念或块的方法。每个导入中究竟有多少类并不重要。重要的是用import语句描述的整体组织。
问题
考虑以下用Python实现的类:
类A(对象):def __init__(self, A =None, b=None): self。A =自我。B = B
这个类可以用来创建列表,如下例所示:
#列表1,2,3 one_two_three = A(1, A(2, A(3))) one_two_three。A #返回1
Jenny编写了下面的函数来创建包含1000个元素的列表,并返回包含最后163个元素的子列表。为了得到正确的答案,她必须用输入来调用它n和米:get_sublist_last_163 (n,米)。
n和米都是非负整数。价值是什么N + m?
def get_sublist_last_163(N, M): #创建一个1000个元素的列表big_list = a (1000) i = 999 while (i>0): big_list = a (i, big_list) i = i - 1 #获取最后163个元素的子列表sublist_last_163 = big_list #调用"b" N次i = 0 while (i < N): sublist_last_163 = sublist_last_163。当(i < M): sublist_last_163 = sublist_last_163时,调用“a”M次i = 0。I = I + 1返回sublist_last_163