对象
对象是称为面向对象编程(简称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
语句定义一个新函数。这个班的名字是才华横溢的
。
Instance是从特定类创建的特定对象。要创建类的实例,您可以使用类名调用类,并传入其参数__init__
方法接受。
1 |
|
的新实例才华横溢的
类。或者换句话说,我们实例化才华横溢的
类
1 |
|
上面定义了方法为才华横溢的
类。方法用于属于类的函数。
这个名字__init__ ()
用于类的“构造函数”。虽然类是新数据类型的蓝图,但您仍然需要创建这种数据类型的值,以便拥有可以存储在变量中或传递给函数的内容。
当被调用时,构造函数创建新对象,运行构造函数中的代码,并返回新对象。这就是user =brilliant ('Mursalin', 17,4)
线。无论类名是什么,构造函数总是命名的__init__
。
另一个常用术语是属性。属性是对象的特征。这个方法叫做__init__ ()
用于初始化对象的属性。就像方法是类中定义的函数一样,属性是类中定义的变量。
类定义中的每个方法都以对实例对象的引用开始。按照惯例,它被命名为“self”。
在Python中,方法的第一个参数自我
。的自我
参数用于创建成员变量。
构造函数的函数体是:
1 2 3 |
|
这看起来有点重复,但这段代码所做的是为构造函数创建的对象创建成员变量。成员变量将以自我
。以显示它们是属于对象的成员变量,而不仅仅是方法中的常规局部变量。
数据
数据指的是我们定义的类的简单变量属性。它们也被称为类变量。它们可以像任何其他变量一样使用,因为它们是在创建类时设置的,并且可以通过类内的方法或程序主部分的其他地方更新。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 15 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变量的值打印到屏幕上。
如果您是面向对象编程的新手,可能会使用Java的'Square()'方法和初始化() 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关键字),只是您已经决定“我想稍后定义这个属性,但我希望我的实例现在完全工作”。还要注意,不仅构造函数实现需要遵守一些特定于语言的约定,而且调用构造函数也需要遵守。在Java中,通过在调用带(或不带)参数的构造函数之前添加键键'new'来调用构造函数方法。在Python中,构造函数是通过向类传递参数来调用的(这实际上会触发初始化方法)。
实现方法
实现方法是最类似于标准过程函数的方法。顾名思义,这是一个方法,你将在其中实现对象的功能。这通常是通过操作对象的属性并提供一些输出来完成的(或者以特定的方式更改一些内部属性,而不提供任何即时输出,这是无限制的)。让我们在我们的Square对象中实现'outputArea'功能:
Python中的实现方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 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()'方法的输出显示边长等于该正方形的sidelth属性的正方形的面积。对象内部方法的基本原则是在其数据内部操作以提供实际功能。任何具有不局限于单个类(或类层次结构)的更通用功能的方法通常在模块中定义(将模块视为旨在提供可应用于许多不同类的功能的方法集合)。如果Square的sidelth属性是4.0而不是3.0,输出将是16.0而不是9.0。该方法也不局限于对其属性执行一些封闭操作,我们可以使用对象的属性和一些任意参数来操作输出,如下所示:
Python中outputArea方法的新实现:
1 2 3 4defoutputAreaTimesN(n):返回(自我。getSideLength()*自我。getSideLength())*n打印广场。outputAreaTimesN(2)#将18.0输出到屏幕
对象变量类型和作用域
在面向对象编程中,方法和变量具有不同的作用域。作用域意味着方法或变量可以或不可以被其他对象或类直接访问。系统可以访问没有实例的类。
类作用域类变量和类方法与一个类相关联。类(对象)的实例不需要使用这些变量或方法。类方法不能访问实例变量或方法,只能访问类变量和方法。
实例范围:实例变量和实例方法与特定对象相关联。它们可以访问类变量和方法。
私人空间私有变量和私有方法只能被包含它们的对象访问。因此,如果出现问题,通常只有一个源文件可以查看。如果您的项目中有一百万行代码,但是您的类保持较小,这可以在很大程度上减少您的bug跟踪工作。
公共空间公共变量和公共方法可以在包含它们的对象之外访问,从实际考虑,这意味着“可能在任何地方”。如果公共字段出了问题,罪魁祸首可能在任何地方,因此为了追踪bug,您可能需要查看相当多的代码
保护范围受保护的变量和方法可以被它们所在的类和继承的类(子类)访问。
封装:提供与对象交互的公共接口,同时在对象内部隐藏其他信息的过程。封装意味着对象的内部表示通常对对象定义之外的视图是隐藏的。的米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一样吗用户
,带有一些附加的方法和成员变量
继承的重要好处是代码重用和降低程序的复杂性。派生类(后代)覆盖或扩展基类(祖先)的功能。
我们在使用继承时应该小心,因为它可能被滥用。我们必须确定,任何可能的变化或更新您的用户
类也是你想要的工作人员
类的所有其他子类用户
也有。
继承描述基例的属性如何由派生类继承。子类继承任何基类的属性,无论这些属性是数据属性还是方法。
下面是一个例子。父
是一个没有属性的简单类。孩子
是派生的类吗父
因此是一个子类。
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 |
|
我们将这个文件保存为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
这个类可以用来创建列表,如下例所示:
#the list 1,2,3 one_two_three = A(1,2,3)) one_two_three。A #返回1
Jenny编写了下面的函数来创建一个包含1000个元素的列表,并返回最后163个元素的子列表。为了得到正确的答案,她必须用输入来调用它n和米:get_sublist_last_163 (n,米)。
n和米都是非负整数。的值是多少N + m?
defget_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_163b i = i + 1 #调用"a" M次i = 0 while (i < M): sublist_last_163 = sublist_last_163。A I = I + 1返回sublist_last_163