在现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体,如学生、汽车等。人类解决问题的方式总是将复杂的事物简单化,于是就会思考这些对象都是由哪些部分组成的。通常都会将对象划分为两个部分,即静态部分与动态部分。
顾名思义,静态部分就是不能动的部分,这个部分被称为“属性”,任何对象都会具备其自身属性,如一个人,其属性包括高矮、胖瘦、年龄、性别等。然而具有这些属性的人会执行哪些动作也是一个值得探讨的部分,这个人可以转身、微笑、说话、奔跑,这些是这个人具备的行为(动态部分),人类通过探讨对象的属性和观察对象的行为来了解对象。
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。
一、面向对象简介
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量:定义在方法中的变量,只作用于当前实例的类。
实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
实例化:创建一个类的实例,类的具体对象。
方法:类中定义的函数。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
二、创建类
Python使用class关键字来定义类,其语法格式如下:
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体
类的帮助信息可以通过ClassName.__doc__查看。
class_suite 由类成员,方法,数据属性组成。
以下是一个简单的 Python 类的例子:
# coding=utf-8
class Cat:
#属性
age = 1
#方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
age 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Cat.age 访问。
self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def prt(me):
print(me)
print(me.__class__)
cat = Cat()
cat.prt();
执行结果为:
从执行结果可以看出,self 代表的是类的实例,代表当前对象的地址,而 self.__class__ 则指向类。
self 不是 python 关键字,我们把他换成 me 也是可以正常执行的:
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def prt(me):
print(me)
print(me.__class__)
cat = Cat()
cat.prt();
三、创建实例对象
实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
# 创建一个对象
tom = Cat()
1、调用对象的方法
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
# 创建一个对象
tom = Cat()
# 调用对象的方法
tom.eat()
tom.drink()
2、添加属性
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
# 创建一个对象
tom = Cat()
# 调用对象的方法
tom.eat()
tom.drink()
# 添加属性
tom.name = "汤姆"
tom.age = 40
3、获取对象的属性
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def introduce(self):
print("%s的年龄是:%d" % (tom.name, tom.age))
# 创建一个对象
tom = Cat()
# 调用对象的方法
tom.eat()
tom.drink()
# 添加属性
# 给tom指向的对象添加2个属性
tom.name = "汤姆"
tom.age = 40
# 获取属性的第1种方式
# print("%s的年龄是:%d"%(tom.name, tom.age))
tom.introduce()
也可以使用以下函数的方式来访问属性:
getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) : 检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。
hasattr(tom, 'age') # 如果存在 'age' 属性返回 True。
getattr(tom, 'age') # 返回 'age' 属性的值
setattr(tom, 'age', 8) # 添加属性 'age' 值为 8
delattr(tom, 'age') # 删除属性 'age'
4、__init__()方法
是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 初始化对象
def __init__(self, new_name, new_age):
self.name = new_name
self.age = new_age
def __str__(self):
return "%s的年龄是:%d" % (self.name, self.age)
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def introduce(self):
print("%s的年龄是:%d" % (self.name, self.age))
# 创建一个对象
tom = Cat("汤姆", 40)
tom.eat()
tom.drink()
# tom.name = "汤姆"
# tom.age = 40
tom.introduce()
baimao = Cat("白猫", 10)
# baimao.name = "白猫"
# baimao.age = 10
baimao.introduce()
5、__str__方法
用于将值转化为适于人阅读的形式
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 初始化对象
def __init__(self, new_name, new_age):
self.name = new_name
self.age = new_age
def __str__(self):
return "%s的年龄是:%d" % (self.name, self.age)
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def introduce(self):
print("%s的年龄是:%d" % (self.name, self.age))
# 创建一个对象
tom = Cat("汤姆", 40)
tom.eat()
tom.drink()
# tom.name = "汤姆"
# tom.age = 40
tom.introduce()
baimao = Cat("白猫", 10)
# baimao.name = "白猫"
# baimao.age = 10
baimao.introduce()
print(tom)
print(baimao)
四、Python内置类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
# coding=utf-8
class Cat:
# 属性
weight = 1.0
# 初始化对象
def __init__(self, new_name, new_age):
self.name = new_name
self.age = new_age
def __str__(self):
return "%s的年龄是:%d" % (self.name, self.age)
# 方法
def eat(self):
print("猫在吃鱼....")
def drink(self):
print("猫正在喝.....")
def sleep(self):
print("猫在睡觉.....")
def introduce(self):
print("%s的年龄是:%d" % (self.name, self.age))
# 创建一个对象
tom = Cat("汤姆", 40)
tom.eat()
tom.drink()
# tom.name = "汤姆"
# tom.age = 40
tom.introduce()
baimao = Cat("白猫", 10)
# baimao.name = "白猫"
# baimao.age = 10
baimao.introduce()
print(tom)
print(baimao)
print("Cat.__doc__:", Cat.__doc__)
print("Cat.__name__:", Cat.__name__)
print("Cat.__module__:", Cat.__module__)
print("Cat.__bases__:", Cat.__bases__)
print("Cat.__dict__:", Cat.__dict__)
五、python对象销毁(垃圾回收)
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。
在 Python 内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。
当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行:
# coding=utf-8
class Dog:
def __del__(self):
print("-----英雄over------")
dog1 = Dog()
dog2 = dog1
del dog1 # 不会调用 __del__方法,因为这个对象 还有其他的变量指向它,即 引用计算不是0
del dog2 # 此时会调用__del__方法,因为没有变量指向它了
print("====================")
# 如果在程序结束时,有些对象还存在,那么python解释器会自动调用它们的__del__方法来完成清理工作
六、类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法
class 派生类名(基类名)
...
在python中继承中的一些特点:
1、如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。详细说明可查看: python 子类继承父类构造函数说明。
2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
3、Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
语法:
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]):
...
# coding=utf-8
class Animal:
def __init__(self):
print("调用父类构造函数")
def eat(self):
print("-----吃----")
def drink(self):
print("-----喝----")
def sleep(self):
print("-----睡觉----")
def run(self):
print("-----跑----")
class Dog(Animal):
def __init__(self):
print("调用子类构造函数")
"""
def eat(self):
print("-----吃----")
def drink(self):
print("-----喝----")
def sleep(self):
print("-----睡觉----")
def run(self):
print("-----跑----")
"""
def bark(self):
print("----汪汪叫---")
class Cat(Animal):
def catch(self):
print("----抓老鼠----")
# a = Animal()
# a.eat()
wangcai = Dog()
wangcai.eat()
tom = Cat()
tom.eat()
继承多个类
class Base(object):
def test(self):
print("----Base")
class A(Base):
def test1(self):
print("-----test1")
class B(Base):
def test2(self):
print("-----test2")
class C(A,B):
pass
c = C()
c.test1()
c.test2()
c.test()
可以使用issubclass()或者isinstance()方法来检测。
issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
七、方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
# coding=utf-8
class Animal:
def eat(self):
print("-----吃----")
def drink(self):
print("-----喝----")
def sleep(self):
print("-----睡觉----")
def run(self):
print("-----跑----")
class Dog(Animal):
def bark(self):
print("----汪汪叫---")
class Xiaotq(Dog):
def fly(self):
print("----飞----")
def bark(self):
print("----狂叫-----")
xiaotq = Xiaotq()
xiaotq.fly()
xiaotq.bark()
xiaotq.eat()
1、基础重载方法
下表列出了一些通用的功能,你可以在自己的类重写:
序号 | 方法, 描述 & 简单的调用 |
1 | __init__ ( self [,args...] ) 构造函数,简单的调用方法: obj = className(args) |
2 | __del__( self ) 析构方法, 删除一个对象,简单的调用方法 : del obj |
3 | __repr__( self ) 转化为供解释器读取的形式,简单的调用方法 : repr(obj) |
4 | __str__( self ) 用于将值转化为适于人阅读的形式,简单的调用方法 : str(obj) |
5 | __cmp__ ( self, x ) 对象比较 ,简单的调用方法 : cmp(obj, x) |
2、运算符重载
Python同样支持运算符重载,实例如下:
# coding=utf-8
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
八、类属性与方法
1、类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
2、类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
3、类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
# coding=utf-8
class Dog:
__age = 0
name = ""
def __init__(self, new_name):
self.name = new_name
self.__age = 0 # 定义了一个私有的属性,属性的名字是__age
def set_age(self, new_age):
if new_age > 0 and new_age <= 100:
self.__age = new_age
else:
self.__age = 0
def get_age(self):
return self.__age
dog = Dog("小白")
# dog.age = -10
# dog.name = "小白"
# print(dog.age)
dog.set_age(10)
age = dog.get_age()
print(age)
# dog.get_name()
# dog.__age = -10
# print(dog.__age) # 报错,实例不能访问私有变量
# object._className__attrName( 对象名._类名__私有属性名 )访问属性
print(dog._Dog__age)
九、单下划线、双下划线、头尾双下划线说明:
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。