面向对象编程思想
Understanding Object-Oriented Programming is one of those things that sets beginner programmers apart from intermediates. The term itself sounds fancy and intimidating, but my hope is that after reading this article, you’ll never want to go back to functional programming.
了解面向对象的编程是使初学者与中间人区别开来的一件事。 这个词本身听起来很花哨和令人生畏,但是我希望,在阅读本文之后,您再也不想回到函数式编程了。
功能与面向对象的编程 (Functional vs. Object-Oriented Programming)
Functional programming is the kind of programming you’ve likely done up to this point. The code runs using only the abstraction of functions. In computer science, an abstraction is an important concept that you will encounter frequently. It involves removing the nitty-gritty details and focusing on big picture functionality. A function is an abstraction, since we only see the function name (without the definition) in the main script. A user can call the function without necessarily understanding how it was implemented.
功能编程是到目前为止您可能已经完成的编程。 该代码仅使用功能抽象来运行。 在计算机科学中, 抽象是一个您会经常遇到的重要概念。 它涉及删除实质性细节,并专注于大图功能。 函数是抽象的,因为我们只能在主脚本中看到函数名(没有定义)。 用户可以调用该功能,而不必了解其实现方式。
There are other kinds of abstractions, one of which is called a class. A class is a collection of related variables (called attributes) and functions (called methods). Classes are useful for combining data and functionality in a process called encapsulation. For example, we can define a class called Person
. We can associate data with a person (name
, age
), as well as functionality (introduce_self()
). We can define this class as follows.
还有其他几种抽象,其中一种称为类。 类是相关变量(称为attribute )和函数(称为method )的集合。 类对于在称为封装的过程中组合数据和功能很有用。 例如,我们可以定义一个名为Person
的类。 我们可以将数据与人( name
, age
)以及功能( introduce_self()
)相关联。 我们可以如下定义此类。
A class is just a user-defined blueprint (just like a function) — it doesn’t contain any user data until we call it and pass it input arguments.
类只是用户定义的蓝图(就像一个函数一样)—在我们调用它并传递输入参数之前,它不包含任何用户数据。
The method defined on line 3 of our class definition, __init__(self, name, age)
, is the constructor method of our class. The constructor method is run when we instantiate a class, like this:
在类定义的第3行__init__(self, name, age)
上定义的方法是我们类的构造方法。 当我们实例化一个类,像这样的 构造 方法运行 :
The constructor method is in charge of assigning input variables to class attributes that can be accessed in any method of the class (without passing them as input arguments to the method). This is done using self
. When a class is instantiated, the instance is called an object — self
is a way of referring to the object from within the class definition, even before it is instantiated. Class attributes can be called from within a class using self.<attribute>
, and class methods can be called from within a class using self.<method>
.
构造函数方法负责将输入变量分配给可以在该类的任何方法中访问的类属性(而不将其作为输入参数传递给该方法)。 这是使用self
完成的。 当实例化一个类时,该实例称为对象 — self
是一种从类定义中甚至在实例化之前就引用对象的方法。 可以使用self.<attribute>
从类内部调用类self.<attribute>
,而可以使用self.<method>
从类内部调用类self.<method>
。
In this case, the variable jane
is an object. We can “tell jane
to do something” (have the object jane
run one of its methods), like so.
在这种情况下,变量jane
是一个对象。 我们可以“告诉jane
做一些事情”(有对象jane
它的方法运行一个),像这样。
The output to the terminal will be the following.
到终端的输出如下。
Hello, my name is Jane. I am 37.
Note that we could have accomplished this task using functional programming. We could have defined a function introduce(name, age)
like this.
注意,我们可以使用函数式编程来完成此任务。 我们可以这样定义一个函数introduce(name, age)
。
If we call the function introduce()
with input arguments “Jane”
and 37
, we get the exact output we had when we created a Person
object and called it’s introduce_self()
method. The difference is that the function introduce()
is not part of any larger abstraction — it exists alone and can be used by anyone, at anytime in a script. The introduce_self()
function exists only within the abstraction of the Person
class. It can only be called using dot-notation on an object (jane.introduce_self()
). More precisely, if we tried to run introduce_self()
on its own, we would get a NameError
telling us the function we are trying to call is not defined.
如果我们使用输入参数“Jane”
和37
调用函数introduce()
,我们将获得创建Person
对象并将其称为introduce_self()
方法时的确切输出。 不同的是,该功能introduce()
不属于任何更大的抽象的一部分-它存在单独,任何人都在脚本中使用,在任何时候。 introduce_self()
函数仅存在于Person
类的抽象内。 只能在对象( jane.introduce_self()
)上使用点符号来调用它。 更准确地说,如果我们尝试自行运行introduce_self()
,则会收到一个NameError
告诉我们正在尝试调用的函数未定义。
面向对象编程的好处 (Benefits Of Object-Oriented Programming)
遗产 (Inheritance)
![Image for post](https://miro.medium.com/max/9999/1*JFIlErUmIGH5U452tEbBpw.png)
Suppose we decide to create two other classes called Engineer
and Teacher
. We want these classes to have all of the same attributes and methods as the Person class plus more attributes and methods. Instead of including the attributes and methods that already exist in the Person
class, we can have the Engineer
and Teacher
classes extend from the Person
class, like this.
假设我们决定创建另外两个类,称为Engineer
和Teacher
。 我们希望这些类具有与Person类相同的所有属性和方法,并具有更多的属性和方法。 像这样,我们可以不将Person
类中已经存在的属性和方法包括在内,而是将Engineer
和Teacher
类从Person
类中扩展出来。
If we do this, then the Engineer
and Teacher
classes will automatically contain all attributes and methods from the Person
class (name
, age
, introduce_self()
), without us having to include that code in the Engineer
and Teacher
class definitions. That is, the following would be a valid line of code.
如果执行此操作,那么Engineer
和Teacher
类将自动包含Person
类的所有属性和方法( name
, age
, introduce_self()
),而无需将这些代码包含在Engineer
和Teacher
类的定义中。 也就是说,以下将是有效的代码行。
The output of this code would be:
该代码的输出为:
Hello, my name is Sally. I am 27.
We call Person
the parent class, and Engineer
and Teacher
the child classes. We say that child classes inherit the attributes and methods of the parent class in a process called inheritance.
我们称Person
为父班 ,而Engineer
和Teacher
为子班 。 我们说,孩子类继承的称为继承过程中的属性和父类的方法。
We can add additional methods to both Engineer
and Teacher
, and they will not be added to Person
. Methods added to Engineer
and Teacher
can have the same name or different names. If they have the same name, that is OK! This is because, in object oriented programming, methods are only defined within the scope of the class — outside of the class, those function names are undefined. For example, we can add a method called describe_profession()
to both the Engineer
and Teacher
class and implement it differently in each. This is a powerful concept in computer science called polymorphism.
我们可以为Engineer
和Teacher
添加其他方法,而这些方法不会添加到“ Person
。 添加到“ Engineer
和“ Teacher
可以具有相同的名称或不同的名称。 如果它们具有相同的名称,那就可以了! 这是因为,在面向对象的编程中,方法仅在类的范围内定义-在类之外,那些函数名未定义。 例如,我们可以向Engineer
和Teacher
类添加一个名为describe_profession()
的方法,并在每个方法中以不同的方式实现它。 在计算机科学中,这是一个强大的概念,称为多态 。
When we create Engineer
and Teacher
objects and call describe_profession()
on each, we will we see different outputs, depending on the type of object (Engineer
or Teacher
).
当我们创建Engineer
和Teacher
对象并在每个对象上调用describe_profession()
时,我们将看到不同的输出,具体取决于对象的类型( Engineer
或Teacher
)。
I code software in C.
I teach High School Pre-Calculus.
Note that we do not need a constructor (the __init__()
method) in child classes. If an attribute or method is not found for a particular class, its parent class is searched.
请注意,子类中不需要构造函数( __init__()
方法)。 如果找不到特定类的属性或方法,则搜索其父类。
It is possible for a child class method to override a parent class method. For example, we can redefine the method introduce_self()
in the Engineer
and Teacher
classes.
子类方法可能会覆盖父类方法。 例如,我们可以在Engineer
和Teacher
类中重新定义方法introduce_self()
。
Now, when we create Engineer
and Teacher
objects and call introduce yourself on them, the function will no longer use introduce_self()
definition in the Person
class.
现在,当我们创建Engineer
和Teacher
对象并对其进行自我介绍时,该函数将不再使用Person
类中的Person
introduce_self()
定义。
Hello, my name is Sally. I am 27. I am an Engineer.
Hello, my name is Jeff. I am 31. I am a Teacher.
If we want to add additional methods to the Engineer
or Teacher
classes, we can just add them as we would any method. However, if we want to add an additional attribute (let’s say, type
) to the Engineer
or Teacher
classes, we need to redefine the constructor(__init__()
). Up to this point, the child classes, Engineer
and Teacher
, have been using the constructor of their parent class (Person
) to instantiate the class and create an object. The constructor of Person
takes two input arguments: name
and age
. In our new constructor, we will include these input arguments and our new input argument (type
). Within the constructor, we need to ensure that all input arguments become attributes of the class. To do this, we write self.type = type
to define our new attribute — but to define the parent attributes, we can just run the parent constructor. We can access the parent of any child object using super()
(which is analogous to how we can use self
to access the object itself). That is, to run the constructor of the parent object, we can just run super().__init__(name, age)
, calling the constructor method of the parent object, and passing the input arguments to the child object on to the parent object. Note that in the following code, I have updated the introduce_self()
methods of the child classes to use our new attribute type
.
如果要向Engineer
或Teacher
类添加其他方法,则可以像添加任何方法一样添加它们。 但是,如果我们想向Engineer
或Teacher
类添加一个附加属性(例如, type
),则需要重新定义构造函数( __init__()
)。 到目前为止,子类Engineer
和Teacher
一直在使用其父类( Person
)的构造函数来实例化该类并创建一个对象。 Person
的构造函数采用两个输入参数: name
和age
。 在我们的新构造函数中,我们将包括这些输入参数和我们的新输入参数( type
)。 在构造函数中,我们需要确保所有输入参数都成为类的属性。 为此,我们编写self.type = type
来定义我们的新属性-但是要定义父级属性,我们只需运行父级构造函数即可。 我们可以使用super()
访问任何子对象的父对象(这类似于我们如何使用self
来访问对象本身)。 也就是说,要运行父对象的构造函数,我们只需运行super().__init__(name, age)
,调用父对象的构造函数方法,并将输入参数传递给子对象,再传递给父对象。 请注意,在以下代码中,我已经更新了子类的introduce_self()
方法以使用我们的新属性type
。
Hello, my name is Sally. I am 27. I am a Software Engineer.
Hello, my name is Jeff. I am 31. I am a High School Teacher.
抽象化 (Abstraction)
![Image for post](https://miro.medium.com/max/9999/1*vZNS91h4ScmFByYeI0EoqA.jpeg)
Inheritance is a means of implementing abstraction. When you create a class, knowledge of its implementation is not necessarily needed for use. For example, any engineer can use our Person
class if we provide them with the call signature and the methods. Together, this information is called an Application Programming Interface (API). The API documentation for our Person
class might look something like this:
继承是实现抽象的一种手段。 创建类时,不一定需要使用它的实现知识。 例如,如果我们为他们提供了呼叫签名和方法,则任何工程师都可以使用我们的Person
类。 这些信息一起被称为应用程序编程接口 ( API )。 我们的Person
类的API文档可能如下所示:
# The Person Class# Syntax
person = Person(name, age)# Attributes
name : str
age : int# Methods
introduce_self()
Any engineer could look at this documentation and use our Person
class, without ever seeing the class code itself. Companies like Google, Amazon, and Apple capitalize on this concept, allowing users to use their software, but not see how it is implemented.
任何工程师都可以查看此文档并使用我们的Person
类, 而无需查看类代码本身 。 像Google,Amazon和Apple这样的公司都利用这一概念,允许用户使用其软件,但看不到其实现方式。
Abstraction is also a useful concept in designing code. It allows you to focus on the big picture of your design, and helps with organization. For example, now that you are familiar with classes and inheritance, the next time you want to write code you should start by creating a software design. (Remember, software is just a set of instructions for your computer, so your Python code is software!) A software design for our Person
/Engineer
/Teacher
classes example (or Person
class hierarchy) might start out looking something like this.
在设计代码时,抽象也是有用的概念。 它使您可以专注于设计的全局,并有助于组织。 例如,既然您熟悉类和继承,那么下次您要编写代码时,应该从创建软件设计开始 。 (请记住,软件只是计算机的一组指令,所以您的Python代码就是软件!)我们的Person
/ Engineer
/ Teacher
类示例(或Person
类层次结构 )的软件设计可能看起来像这样。
![Image for post](https://miro.medium.com/max/9999/1*XAWSoae--Ri4F7Gb-vffGg.png)
Person
class hierarchy.
Person
类层次结构的软件设计。
We could expand on our design by adding attributes and methods to each class.
我们可以通过向每个类添加属性和方法来扩展设计。
![Image for post](https://miro.medium.com/max/9999/1*G0C_NwNi-Tl6vHfzFdaH3g.png)
This visual representation of our Person
class hierarchy is called a Unified Modeling Language (UML) Class Digram. UML Class Diagrams are a useful software design technique, providing information about the relationship between classes in the hierarchy, in addition to the attributes and methods associated with each. This is helpful for understanding the design on a high-level, without worrying about the details of implementation (which is the definition of abstraction). You can make this kind of diagram before you even write any code.
我们Person
类层次结构的这种直观表示形式称为统一建模语言 ( UML ) 类Digram 。 UML类图是一种有用的软件设计技术,除了提供与每个类关联的属性和方法外,还提供有关层次结构中类之间关系的信息。 这有助于从高层次理解设计,而无需担心实现细节 (即抽象的定义)。 您甚至可以在编写任何代码之前就制作出这种图表。
![Image for post](https://miro.medium.com/max/9999/1*18i_MJhsW8ZMEcGLhNbA-g.jpeg)
面向对象的编程语言 (Object-Oriented Programming Languages)
An object-oriented programming language is a programming language built entirely on objects. That is, no variables or functions can exist unless they exist within an object. Python is unique in that, while it is considered an object-oriented programming language, it also supports functional programming. Many languages only (unless you includes lambdas) support object-oriented programming. That is, you can’t write a function and then use that function in a script — the function needs to be contained within an object (making it a method). Some such languages include:
面向对象的编程语言是一种完全基于对象的编程语言。 即,除非变量或函数存在于对象中,否则它们将不存在。 Python的独特之处在于,尽管它被认为是一种面向对象的编程语言,但它也支持函数式编程。 仅许多语言(除非包括lambdas )支持面向对象的编程。 也就是说,您不能编写函数然后在脚本中使用该函数-该函数需要包含在一个对象中(使其成为方法)。 这些语言包括:
- Java Java
- C++ C ++
最后的想法 (Final Thoughts)
Ultimately, it is up to you whether or not you take an object-oriented or functional approach to your project. Making that decision really comes down to the size of your project. If it is a big undertaking, an object-oriented approach will help with organization and future development. If it is just a matter of making a few computations, a functional approach might make more sense. At least now, after reading this, you can hopefully make an informed decision.
最终,由您决定是否对项目采用面向对象或功能的方法。 做出决定实际上取决于您的项目规模。 如果这是一项艰巨的任务,那么面向对象的方法将有助于组织和未来的发展。 如果只是进行一些计算,那么功能方法可能更有意义。 至少现在,阅读本文后,您可以做出明智的决定。
翻译自: https://medium.com/simple-cs/object-oriented-programming-7e35b0b2ff8e
面向对象编程思想