什么是面向对象编程(OOP)?
由于Python是一种多范式编程语言(面向对象编程、函数式编程)。
我们常见的一种编程范例是函数式编程(面向过程),其构造类似于顺序执行的程序,因为它以函数和代码块的形式提供一组执行步骤,这些步骤一步步执行以完成任务。
面向对象编程(Object-oriented Programming,简称OOP)是另一种编程范例,它提供了一种结构化程序的方法,以便将属性和行为捆绑到单个对象中。例如,对象可以表示具有姓名、年龄、地址等属性的人,具有行走、说话、呼吸和跑步等行为。或者包含收件人列表、主题、正文等属性的电子邮件,以及添加附件和发送等行为。
换句话说,面向对象编程是一种, 可以为具体现实世界的事物建模的方法,如汽车以及公司和员工,学生和教师等事物之间的关系. OOP将现实世界的实体建模为软件对象,以及与之相关的数据,并可以执行某些功能。
关键的一点是,对象是面向对象编程范例的核心,不仅在函数编程中表示数据,而且在程序的整体结构中也是如此。
Python面向对象编程
面向对象技术简介:
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
方法:类中定义的函数。
实例化:创建一个类的实例,类的具体对象。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
类属性:类属性在整个实例化的对象中是公用的。类属性定义在类中且在函数体之外。类属性通常不作为实例属性使用。
实例属性:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,一个Dog类型的对象派生自Animal类,可以说,Dog是一个Animal。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
Python中的类
首先关注数据,Python中可用的原始数据结构(如数字,字符串和列表)旨在分别表示简单的事物,例如列举喜欢的颜色。
如果你想代表更复杂的东西怎么办? 例如,假设您想跟踪许多不同的动物。如果您使用了列表,则第一个元素可以是动物的名称,而第二个元素可以表示其年龄。你怎么知道哪个元素应该是哪个?如果你有100种不同的动物怎么办?你确定每只动物都有名字和年龄,等等吗?如果你想为这些动物添加其他属性怎么办?这就是为什么我们需要一个"类"(Class)。
类可以用来创建新的用户定义的数据结构,其中包含有关内容的任意信息。对于动物,我们可以创建一个Animal( )类来跟踪关于Animal的属性,如名称和年龄。
注意, 一个类只提供结构 - 它是应该如何定义某个东西的蓝图,但它实际上并不提供任何真实的内容。 Animal( )类可以指定名称和年龄, 是定义动物所必需的,但它不会包含特定动物的实际名字或年龄。
如何在Python中定义类
使用 class 语句来创建一个新类,class 之后为类的名称(使用骆驼命名法,以大写字母开头),最后以冒号结尾:
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体
例:
class Dog:
pass
我们在这里使用了Python关键字pass。这经常被用作代码最终会占用的占位符。它允许我们运行此代码, 而不会抛出错误。
注意:上面的代码在Python 3上是正确的。在Python 2.x(“遗留Python”)上,您将使用稍微不同的类定义:
#Python 2.x类定义: class Dog(object): Pass
括号中的(对象)部分指定了您继承的父类(更多内容见下文。)在Python 3中,这不再是必需的,因为它采用隐式默认值。
类的帮助信息可以通过ClassName.__doc__查看。
class_suite 由类成员,方法,数据属性组成。
class Dog:
'Dog'
pass
Dog.__doc__
#'Dog'
Python对象(实例)
类的蓝图已经有了,需要用的时候需要创建副本,也就是类的对象,这个过程叫实例化,是将抽象的类实例化为实在的对象。比如它是一只真正的动物,就像一只名叫罗杰的狗,已经八岁了。
换句话说,类就像一个表格或问卷。它定义了所需的信息。填写表格后,副本就是该类的一个实例,它包含相关的实际信息。
您可以填写多个副本以创建许多不同的实例,但如果没有表单作为指导,您将会彻底迷失,不知道需要哪些信息。因此,在创建对象的单个实例之前,我们必须首先通过定义类来指定所需的内容。
实例属性
使用__init __()方法通过为对象的初始属性提供其默认值(或状态),来初始化(指定)对象的初始属性。__init __()方法被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
self 代表类的实例,self 在定义类的方法时是必须有的。此方法必须至少有一个参数以及自变量,它引用对象本身(例如,Dog)。
class Dog:
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
在Dog()类中,每只狗都有一个特定的名字和年龄,这对于你何时开始真正创造不同的狗来说显然很重要。请记住:该类仅用于定义狗,而不是实际创建具有特定名称和年龄的个体狗的实例。
注意:您永远无需主动调用__init __()方法;当你创建一个新的’Dog’实例时会自动调用它。
类属性
虽然实例属性特定于每个对象,但类属性对于所有实例都是相同的 – 在这种情况下,属性都来自狗。
class Dog:
# Class Attribute
species = 'mammal'
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
因此,虽然每只狗都有一个独特的名字和年龄,但每只狗都是哺乳动物。
实例化对象
实例化是创建一个新的,唯一的类实例的意思。Python中,类的实例化类似函数调用方式。
>>> class Dog:
... pass
...
>>> Dog()
<__main__.Dog object at 0x1004ccc50>
>>> Dog()
<__main__.Dog object at 0x1004ccc90>
>>> a = Dog()
>>> b = Dog()
>>> a == b
False
我们首先定义一个新的Dog()类,然后创建两个新的狗,每个狗分配给不同的对象。因此,要创建类的实例,请使用类名,后跟括号。然后为了证明每个实例实际上是不同的,我们实例化了两个狗,将每个狗分配给一个变量,然后测试这些变量是否相等。
class Dog:
pass
#Dog()#?????????????
#print(Dog())
#print(Dog())
#print(Dog())
a = Dog()
b = Dog()
print(a == b)
print(a)
print(b)
#False
#<__main__.Dog object at 0x7f5b38859b10>
#<__main__.Dog object at 0x7f5b38859b50>
您认为类实例的类型是什么?
>>> class Dog:
... pass
...
>>> a = Dog()
>>> type(a)
<class '__main__.Dog'>
访问属性
我们可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:
class Dog:
# Class Attribute 类属性
species = 'mammal'
# Initializer / Instance Attributes 实例属性
def __init__(self, name, age):
self.name = name
self.age = age
# Instantiate the Dog object 实例化
philo = Dog("Philo", 5)
mikey = Dog("Mikey", 6)
# Access the instance attributes 打印实例属性
print("{} is {} and {} is {}.".format(
philo.name, philo.age, mikey.name, mikey.age))
# Is Philo a mammal?
if philo.species == "mammal":
print("{0} is a {1}!".format(philo.name, philo.species))
#Philo is 5 and Mikey is 6.
#Philo is a mammal!
我们创建了Dog()类的新实例,并将其分配给变量philo。然后我们采用“Philo”和5,分别代表狗的名字和年龄。
这些属性将传递给__init__方法,该方法在您创建新实例时将其调用,并将名称和年龄附加到对象。
这是Python魔法: 当你创建一个新的类实例时,Python会自动确定self是什么(在本例中是一个Dog)并将其传递给__init__方法。
定义类的方法
在类的内部,使用 def 关键字可以为类定义一个方法。
与一般函数定义不同,类方法必须包含参数 self,且为第一个参数(与__init__方法一样)。
它们还可用于使用对象的属性执行操作。
如下例子中,我们定义了两种方法,在后一种方法中,我们定义了行为speak()。
class Dog:
# Class Attribute
species = 'mammal'
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# Instantiate the Dog object
mikey = Dog("Mikey", 6)
# call our instance methods
print(mikey.description())
print(mikey.speak("Gruff Gruff"))
#Mikey is 6 years old
#Mikey says Gruff Gruff
修改属性
您可以根据某些行为(方法)更改属性的值:
在这里,我们添加了一种发送电子邮件的方法,该方法将is_sent变量更新为True。
class Email:
def __init__(self):
self.is_sent = False
def send_email(self):
self.is_sent = True
my_email = Email()
print(my_email.is_sent)
my_email.send_email()
print(my_email.is_sent)
#False
#True
Python类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
继承是一个类采用另一个类的属性和方法的过程。通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
重要的是要注意子类覆盖或扩展父类的功能(例如,属性和行为)。换句话说,子类继承了父项的所有属性和行为,但也可以添加不同行为。最基本的类是一个对象,通常所有其他类都继承为它们的父对象。
定义新类时,Python 3隐式使用object作为父类。所以以下两个定义是等价的:
class 派生类名(基类名)
...
class Dog(object):
pass
# In Python 3, this is the same as:
class Dog:
pass
Dog示例
让我们假装我们在一个公园,有多个Dog对象, 发起不同的Dog行为,每个对象都有不同的属性。一般来说,这意味着有些狗正在跑步,而有些正在伸展,有些正在和其他狗玩耍。此外,每只狗都由它的主人命名,并且由于每只狗都是活生生的, 各个年龄段的都有。
先来定义两只不同品种的狗:
class Dog:
def __init__(self, breed):
self.breed = breed
spencer = Dog("German Shepard")#德国牧羊犬
print(spencer.breed)
sara = Dog("Boston Terrier")#波士顿小猎犬
print(sara.breed)
#German Shepard
#Boston Terrier
扩展父类的功能
每种狗的行为略有不同。考虑到这些因素,让我们为每个品种创建单独的类。这些是父类Dog的子类。
我们没有添加任何特殊属性或方法来区分RussellTerrier和Bulldog,但由于它们现在是两个不同的类,我们可以为它们添加一个速度的类属性。
完成程序时,请仔细阅读代码, 以搞清其中的原理,然后在运行程序之前,先在大脑中预测一下输出结果, 然后和真正的输出结果比对一下, 看看是否一致。
运行下方代码:
#父类
class Dog:
# Class attribute
species = 'mammal'
# Initializer / Instance attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
#子类
# Child class (inherits from Dog class)
class RussellTerrier(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# Child class (inherits from Dog class)
class Bulldog(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
#实例化
# Child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
print(jim.description())
# Child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))
#Jim is 12 years old
#Jim runs slowly
jim和julie都是Dog()类的实例,而johnnywalker不是Bulldog()类的实例(是RussellTerrier()类的实例)。
作为一个完整性检查,我们测试了julie是否是jim的实例,这是不可能的,因为jim是类的实例而不是类本身 - 因此是TypeError的原因。
覆盖父类的功能
请记住,子类也可以覆盖父类的属性和行为。举些例子:
class Dog:
species = 'mammal'
class SomeBreed(Dog):
pass
class SomeOtherBreed(Dog):
species = 'reptile'
frank = SomeBreed()
print(frank.species)
beans = SomeOtherBreed()
print(beans.species)
#mammal
#reptile
SomeBreed()类从父类继承物种,而SomeOtherBreed()类覆盖物种,将其设置为爬行动物.
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
class Parent: # 定义父类
def myMethod(self):
print('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print('调用子类方法')
c = Child() # 子类实例
c.myMethod() # 子类调用重写方法
#调用子类方法
父类与子类
isinstance()函数用于确定实例是否也是某个父类的实例。
# Parent class
class Dog:
# Class attribute
species = 'mammal'
# Initializer / Instance attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# Child class (inherits from Dog() class)
class RussellTerrier(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# Child class (inherits from Dog() class)
class Bulldog(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# Child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
print(jim.description())
#Jim is 12 years old
# Child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))
#Jim runs slowly
# Is jim an instance of Dog()?
print(isinstance(jim, Dog))
#True
# Is julie an instance of Dog()?
julie = Dog("Julie", 100)
print(isinstance(julie, Dog))
#True
# Is johnny walker an instance of Bulldog()
johnnywalker = RussellTerrier("Johnny Walker", 4)
print(isinstance(johnnywalker, Bulldog))
#False
# Is julie an instance of jim?
print(isinstance(julie, jim))
#报错,第二个参数必须是类名。isinstance() arg 2 must be a type or tuple of types
Python内置类属性
__dict __ : 类的属性(包含一个字典,由类的数据属性组成)
__doc __ :类的文档字符串
__name __: 类名
__module __: 类定义所在的模块(类的全名是’__main __.className’,如果类位于一个导入模块mymod中,那么className.__module __ 等于 mymod)
__bases __ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
print("Employee.__doc__:", Employee.__doc__)#打印文档字符串
print("Employee.__name__:", Employee.__name__)#打印类名
print("Employee.__module__:", Employee.__module__)#打印类所在模块
print("Employee.__bases__:", Employee.__bases__)#打印类所有父类构成的元组
print("Employee.__dict__:", Employee.__dict__)#打印类属性
#Employee.__doc__: 所有员工的基类
#Employee.__name__: Employee
#Employee.__module__: __main__
#Employee.__bases__: (<class 'object'>,)
#Employee.__dict__: {'__module__': '__main__', '__doc__': '所有员工的基类', 'empCount': 0, '__init__': <function Employee.__init__ at 0x7f5b387de200>, 'displayCount': <function Employee.displayCount at 0x7f5b387de290>, 'displayEmployee': <function Employee.displayEmployee at 0x7f5b387de320>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
- 结论
OOP是一种编程范例,而不是Python概念。大多数现代编程语言,如Java,C#,C ++都遵循OOP原则。所以好消息是学习面向对象的编程基础知识对你来说在各种情况下都很有价值 - 无论你是否使用Python