本文代码地址github.com
面向对象概念类(class): 用来描述具有相同的属性和方法的对象的集合,它定义了该集合中每个对象所共有的属性和方法,对象是类的实例
方法: 类中定义的函数
类变量: 类变量是类的所有对象共有的属性,它不是某个具体对象特有的属性,类变量定义在类中且在函数体之外
数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据
方法重载: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的重载(override)
继承: 一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待
实例化: 创建一个类的实例,类的具体对象
对象: 通过类定义的数据结构实例
类是对象的蓝图和模板,对象是类的具体实例,类提供了一种组合数据和功能的方法,创建一个新类意味着创建一个新的对象类型,从而允许创建一个该类型的新实例,每个类的实例可以拥有保存自己状态的属性,一个类的实例也可以有改变自己状态的(定义在类中的)方法
类定义
类定义的语法格式如下
class ClassName:
.
.
.
在Python中我们通过obj.name和obj.method的语法引用对象属性和方法,我们通过如下简单的例子演示属性引用和方法调用
class SimpleClass:
"""一个简单的类"""
# name是类变量,所有实例共享该类变量
name = "张三"
def fun(self):
"""类中普通方法定义以self作为第一个参数,self表示的就是当前实例"""
return "Python is fun!"
# 实例化类
x = SimpleClass()
# 访问类的属性和方法
print("SimpleClass类的name为: ", x.name)
print("SimpleClass类的name为: ", SimpleClass.name) # 类变量既可以通过实例访问也可以通过类来访问
print("SimpleClass类的方法fun输出为: ", x.fun())
SimpleClass类的name为: 张三
SimpleClass类的name为: 张三
SimpleClass类的方法fun输出为: Python is fun!
类有一个名为__init__()的特殊方法(构造方法),该方法在类实例化时会自动调用,__init__()方法可以有参数,参数通过 __init__() 传递到类的实例化操作上
class Person:
def __init__(self, name, age, gender):
"""在实例化对象时将会调用该方法,在实例化时传递name,age,gender等参数构造对象"""
self.name = name
self.age = age
self.gender = gender
def print_info(self):
"""简单打印属性"""
print("我叫{},性别{},今年{}岁".format(self.name, self.gender, self.age))
# 传递参数构造对象,在构造对象的时候会调用__init__()方法
p1 = Person("张三", 18, "男")
# 调用对象方法
p1.print_info()
p2 = Person("李四", 19, "男")
# 通过obj.name的方式访问对象的属性
print("姓名: ", p2.name)
print("年龄: ", p2.age)
print("性别: ", p2.gender)
我叫张三,性别男,今年18岁
姓名: 李四
年龄: 19
性别: 男
Python作为一种动态语言,还可以动态的添加属性
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self):
# 在对象内部,__init__()以外的地方动态添加属性
self.gender = "男"
print("我叫{},性别{},今年{}岁".format(self.name, self.gender, self.age))
# 构造对象
p = Person("张三", 19)
# 在对象外动态添加属性
p.city = "北京"
访问控制
与其它主流面向对象编程语言不同,Python中不存在public,protected,private这样的关键字来限定属性和方法的访问,在Python中通过在属性和方法名字前面添加两个下划线来将属性和方法设置为私有的,在Python中可以通过property装饰器为私有属性提供读取和修改的方法,装饰器通常会放在类、函数或方法的声明之前,通过一个@符号表示将装饰器应用于类、函数或方法,派生类不会继承基类的私有属性和方法.
class Person:
def __init__(self, name, age, gender):
# 以两个下划线开头定义的属性是private属性,private属性不能直接被外部访问
self.__name = name
self.__age = age
self.__gender = gender
# 定义属性访问器(getter)
@property
def name(self):
return self.__name
# 定义属性修改器(setter)
@name.setter
def name(self, name):
# isinstance可用于检查一个对象的类型,这里要求name必须是字符串类型
if not isinstance(name, str):
raise TypeError("{}必须是字符串类型".format(name))
self.__name = name
@property
def age(self):
return self.__age
p = Person("张三", 19, "男")
# 通过getter访问私有属性
print(p.name)
# 通过setter修改私有属性
p.name = "李四"
print(p.name)
# 定义了getter,访问ok
print(p.age)
# 没有定义setter直接访问私有属性将抛出AttributeError
p.age = 17
张三
李四
19
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in
34 print(p.age)
35 # 没有定义setter直接访问私有属性将抛出AttributeError
---> 36 p.age = 17
AttributeError: can't set attribute
静态方法与类方法
之前我们在类中定义的方法都是对象方法,在Python中还可以定义静态方法和类方法,使用@staticmethod装饰器定义静态方法,使用@classmethod装饰器定义类方法,静态方法和类方法与对象方法的主要区别是静态方法和类方法是类的固有属性,它们不依赖,不特定于具体的实例,我们通过例子来看它们的定义和使用
# 静态方法和类方法与具体的实例无关,它们往往实现一些服务于该类的功能性逻辑,且该功能性逻辑对该类的所有实例来说都是一样的
# 对象方法与具体的实例相关,同一个类的不同实例调用同一个对象方法往往产生不同结果
class Triangle:
"""一个三角形类"""
def __init__(self, a, b, c):
"""a,b,c参数为三角形的三条边"""
self.a = a
self.b = b
self.c = c
# 通过@staticmethod定义静态方法
@staticmethod
def is_valid_static(a, b, c):
"""
静态方法没有self和cls参数
该函数实现的逻辑是验证输入的三条边是否能构成一个三角形
验证三条边能否构成三角形是三角形类本身固有的功能属性,与具体什么三角形实例无关,因而使用静态方法或类方法
"""
return a + b > c and b + c > a and a + c > b
# 通过@classmethod定义类方法
@classmethod
def is_valid_class(cls, a, b, c):
"""
与对象方法不同的是,类方法第一个参数为cls,cls绑定的是类
该函数实现的逻辑是验证输入的三条边是否能构成一个三角形
验证三条边能否构成三角形是三角形类本身固有的功能属性,与具体什么三角形实例无关,因而使用静态方法或类方法
"""
return a + b > c and b + c > a and a + c > b
def perimeter(self):
"""
对象方法第一个参数为self,self绑定的是当前实例化的对象
该函数计算三角形的周长
计算三角形的周长与特定的三角形实例相关,因为不同的三角形它们的周长不同,因而必须定义成对象方法
"""
return self.a + self.b + self.c
a, b, c = 3, 4, 5
# 通过类名.静态方法名调用静态方法,静态方法不依赖于具体实例
print(Triangle.is_valid_static(a, b, c))
# 通过类名.类方法名调用类方法,类方法不依赖于具体实例
print(Triangle.is_valid_class(a, b, c))
# 构造三角形实例
t = Triangle(a, b, c)
# 三角形的周长与具体三角形相关
print(t.perimeter())
True
True
12
继承与多态
面向对象编程语言支持在已有类的基础上创建新类,从而减少重复代码的编写,提供继承信息的类叫做父类(也叫基类),得到继承信息的类叫做子类(也叫派生类),派生类除了继承基类属性和方法外还可以重载基类的方法
派生类定义的语法格式如下
class DerivedClassName(BaseClassName):
.
.
.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self):
print("name: {}, age: {}".format(self.name, self.age))
def gender(self):
"""基类可以定义接口供派生类实现,pass语句表示什么也不做"""
pass
class Male(Person):
def __init__(self, name, age):
# 调用基类构造方法,通过super()调用基类方法
super().__init__(name, age)
def gender(self):
"""重载基类方法"""
print("男童鞋")
def say_hello(self):
"""派生类可以继续定义自己的方法"""
print("Hello Boy")
class Female(Person):
def __init__(self, name, age):
# 调用基类构造方法,通过super()调用基类方法
super().__init__(name, age)
def gender(self):
"""重载基类方法"""
print("女童鞋")
def say_byebye(self):
"""派生类可以继续定义自己的方法"""
print("byebye")
p = Person("老张", 50)
p.print_info()
m = Male("张三", 25)
# 调用从基类继承过来的方法
m.print_info()
# 调用重载的方法
m.gender()
# 调用派生类自己定义的方法
m.say_hello()
f = Female("小张", 22)
# 调用从基类继承过来的方法
f.print_info()
# 调用重载的方法
f.gender()
# 调用派生类自己定义的方法
f.say_byebye()
name: 老张, age: 50
name: 张三, age: 25
男童鞋
Hello Boy
name: 小张, age: 22
女童鞋
byebye