前言
我们前面讲完了全栈开发的第一部分——基础,今天我们说说第二部分——面向对象编程。我们从什么是类,什么是对象,以及面向对象的三要素等方面来说说。
面向对象
首先需要明确,面向对象编程不是python独有的;
面向对象是一种编程思想;
在面向对象的思想中
万物都是对象
面向对象的简单理解:
面向对象是把一组数据结构和处理他们的方法组成对象,把具有相同行为的对象归纳成类,通过封装隐藏类的内部细节,通过继承使类得到泛化,通过多态实现基于对象类型的动态分类。
类是什么
类表示一组(或一类)对象,每个对象都属于特定的类,并被称为该类的实例。
在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。
编写类时,你定义一大类对象都有的通用行为。基于类创建 对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。根据类来创建对象被称为 实例化,这让你能够使用类的实例
在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法;对象由属性和方法组成。属性不过是属于对象的变量,而方法是存储在属性中的函数。
- python中类的创建
示例如下
class Person(): def __init__(self): pass def set_name(self, name): self.name = name def get_name(self): return self.name def greet(self): print("Hello, world! I'm {}.".format(self.name))
Person是类的名称,包含三个方法定义, class 语句创建独立的命名空间,用于在其中定义函数。参数 self 指向对象本身。
方法 init() 是一个特殊的方法,每当创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
self 很有用,甚至必不可少。如果没有它,所有的方法都无法访问对象本身——要操作的属性所属的对象;
默认情况下,可从外部访问对象的属性。若让属性不能从对象外部访问,可将属性定义为私有。私有而只能通过存取器方法来访问
Python没有为私有属性提供直接的支持,要让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可
对象是什么
对象就是真实世界中的实体,对象与实体是一一对应的,也就是说现实世界中每一个实体都是一个对象,它是一种具体的概念。对象有以下特点:
对象具有属性和行为。
对象具有变化的状态。
对象具有唯一性。
对象都是某个类别的实例。
对象就是通过类定义的数据结构实例
person = Person()
python对象中的类成员
类的成员主要包括:
字段
方法
属性
字段:
字段包括:普通字段和静态字段,使用和定义都是不一样,其最本质的区别就是内存中保存的位置不同。
普通字段属于对象
静态字段属于类
class Person(): type = 'ren' #静态字段 def __init__(self): #普通字段 self.name = 'mingzi' pass def set_name(self, name): self.name = name p = Person() p.type = 'woman' p.name = 'man' print(Person.type) print(p.name)
在以上示例代码中:
type是静态字段,可通过类名之间调用
name是普通字段,需要通过实例对象调用
通常情况下我们都使用普通字段,当一个变量在类的所有对象中共同使用,而且数据共享的时候,我们就可以使用静态字段。
方法
方法包括:普通方法、静态方法和类方法。
普通方法:由对象调用;包含一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
类方法:由类调用; 包含一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;使用@classmethod装饰器
静态方法:由类调用;没有默认参数;使用@staticmethod装饰器
代码示例:
class Human: count = 0 def __init__(self, name, age): self.name = name self.age = age #普通方法 def setName(name): self.name=name @classmethod def add_count(cls): cls.count += 1 # 可以通过类名修改类属性 @classmethod def get(cls): return cls.count @staticmethod def static_func(): # 静态方法 print("普通函数")
普通方法中可以直接使用对象的普通字段
类方法中可以直接使用静态字段
静态方法中不能直接使用普通字段和静态字段
属性
python中的属性就是普通方法的变种,就是将一个方法伪装成一个属性;
- 属性定义的两种方式:
装饰器:在方法上应用装饰器
静态字段:在类中定义值为property对象的静态字段
面向对象三要素
封装:
通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
继承:
继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
多态:
多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。
封装
执行以下代码:
class Person(): def __init__(self): pass def set_name(self, name): self.__name = name def get_name(self): return self.__name def greet(self): print("Hello, world! I'm {}.".format(self.name)) p = Person() p.set_name('test') print(p.get_name())
继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个父类(单继承)或多个父类(多继承)
代码示例:
class Person(): def __init__(self): pass def set_name(self, name): self.name = name def get_name(self): return self.name def greet(self): print("Hello, world! I'm {}.".format(self.name)) class Boy(Person): def run(self): print('i can run') def greet(self): print('i am subclass boy') b =Boy() b.name = '张三' b.greet()
多态
- 多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不用考虑而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名:
- 多态已封装和为前提继承
在上面代码基础上新增以下代码:
class Girl(Person): def say(self): print('i can good girl') def greet(self): print('i am subclass Girl')
Boy,Girl都继承自Person,三个对象都有同样的方法名greet();
Boy和Girl重写了父类的方法,实现不同的输出。
创建类
首先,我们创建一个类,示例如下。
创建一个类:
# -*- coding: UTF-8 -*- from random import choice class Divergence: def __init__(self,name='robot'): self.name = name pass def getChoice(self,name=None): if name is not None: self.name = name self.__lists = ['剪刀','石头','布'] result = choice(self.__lists) print(self.name,':选择了>',result)
定义类时,如果没有手动添加 init() 构造方法,又或者添加的 init() 中仅有一个 self 参数,则创建类对象时的参数可以省略不写。
在上面的代码中,由于构造方法除 self 参数外,还包含 name参数,且设置了默认参数,因此在实例化类对象时,按需传入相应的 name 值(self 参数是特殊参数,不需要手动传值,Python 会自动传给它值)
类对象的使用
定义的类只有进行实例化,也就是使用该类创建对象之后,才能得到利用。总的来说,实例化后的类对象可以执行以下操作:
访问或修改类对象具有的实例变量,甚至可以添加新的实例变量或者删除已有的实例变量;
调用类对象的方法,包括调用现有的方法,以及给类对象动态添加方法。
下面验收如何使用:
#实例化对象的时候传递name参数 d = Divergence('A') d.getChoice() #实例化时不传递参数 d = Divergence() #调用方法时传递 d.getChoice('A')
我们通过实例来说明如何创建对象。
创建点对象,有多种方式创建一个类的实例,我们称一个点的x、y、z都一样的点为对角点。
# -*- coding: UTF-8 -*- class Point: # TODO(You): 添加代码支持类的创建 if __name__ == '__main__': points = [] # TODO(You): 批量创建1000个对角点,第i个点的坐标是 (i,i,i) for point in points: print(point)
可以这样补全代码
class Point: def __init__(self, x, y, z): self.x = x self.y = y self.z = z if __name__ == '__main__': points = [] for i in range(1000): points.append(Point(i,i,i))
抽象类
抽象类概念:
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化,需要借助python模块实现;
抽象类是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法
python中的abc模块
- python中需要利用abc模块实现抽象类
- 抽象类的本质仍是类,指的是一组类的类似性,包括数据属性(如all_type)和函数属性;
- 抽象类是一个介于类和接口之间的一个概念,同时具有类和接口的部分特性
下面通过具体python题目说明抽象类的概念
使用 abc 库,创建抽象 BasePoint 类,创建 Point 子类,实现 dot 抽象方法,计算内积
# -*- coding: UTF-8 -*- import abc # TODO(You): 请实现抽象类 BasePoint class Point(BasePoint): def __init__(self, x, y, z) -> None: super().__init__(x, y, z) def dot(self, right): return self.x*right.x+self.y*right.y+self.z*right.z if __name__ == '__main__': p1 = Point(0, 1, 2) p2 = Point(2, 4, 6) assert p1.dot(p2) == 16 p1 = BasePoint(0, 1, 2) p2 = BasePoint(2, 4, 6) assert p1.dot(p2) is None
补全代码如下
class BasePoint: def __init__(self, x, y, z) -> None: self.x = x self.y = y self.z = z @abc.abstractmethod def dot(self, right): pass
访问限制
为什么要做访问限制
做访问限制,是为了程序的健壮性。如果可以从外部对函数里面重要的属性进行任意修改,有可能程序崩溃只是因为一次不经意地参数修改。
- 通过定义私有属性做访问限制
默认情况下,可从外部访问对象的属性。若让属性不能从对象外部访问,可将属性定义为私有。
私有属性只能通过存取器方法来访问
Python没有为私有属性提供直接的支持,要让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可
class Apple(): def name(self,name): self.__name = name def getName(self): return self.__name
- 通过定义私有函数做访问限制;
与属性一样,只需要在函数名前面添加两个下划线打头既可实现私有函数;
class Apple(): def __setAge(self,age): self.__age = age def getName(self): return self.__name def info(self,age): self.__setAge(age);
- 特殊情况:
在Python中,以双下划线开头,并且以双下划线结尾的变量是特殊变量如__name__,特殊变量是可以直接访问的;
按照约定俗成的规定,以一个下划线开头的实例变量名(例如_category) 外部也是可以直接访问的,但是这个形式的变量表达的意思是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”
下次我们说说MYSQL数据库。