Python – 类
部分内容参考菜鸟学院,致谢!!!
Python的面向对象技术
- 类(class):用来描述具有相同属性和方法对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及实例对象的相关数据。
- 方法重写:如果从父类(超类)继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 继承:即一个派生类(derived class)继承基类(base class)(也叫作超类)的字段和方法。继承也允许把一个派生类的对象作为基类对象对待。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 实例化:创建一个类的实例,类的具体对象
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构的实例。对象包括两个数据成员(类变量和实例变量)和方法。
如何建立一个类
以下面的例子为例,讲述如何构建一个python的类。
建立一个类,类名为Person,完成以下功能
1. 能获取实例的名字
2. 能显示实例的名字
3. 能打招呼
#!/usr/bin/env python
class Person:
def __init__(self):
pass
def getName(self, name): #get name
self.name = name;
def showName(self): #showName
print "my name is %s" % self.name
def greeting(self): #greeting
print "hello, good morning, I'm %s" % self.name
zach = Person()
zach.getName("zach")
zach.showName()
zach.greeting()
- self:是对类对实例自身的引用,指向实例本身。
- init()方法是一种特殊的方法,被称为类的构造函数或者初始化方法,当创建这个类的实例时就会自动调用该方法
结果如下:
访问属性
完整的实例代码如下:
- 访问属性
实例代码如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Person:
number = 0;
def __init__(self, name, age):
self.name = name
self.age = age
Person.number += 1
def showName(self): #showName
print "my name is %s" % self.name
def showNum():
print "Total Person %d" % Person.number
"创建person类的对象"
zach = Person("zach", 27)
angle = Person("angle", 28)
"访问属性"
zach.showName()
angle.showName()
print "Total Person is %d" % Person.number
结果如下:
my name is zach
my name is angle
Total Person is 2
- 添加、删除和修改类的属性
zach.height = 178 #添加一个"height"属性
zach.height = 168 #修改"height"属性
del zach.height.age #删除"height"属性
也可以通过以下函数的方式来访问属性:
- getattr(obj, name[, default]):访问对象属性
- hasattr(obj, name):检查是否存在一个属性
- setattr(obj, name, value):设置一个属性。如果属性不存在,会创建一个新属性
- delattr(obj, name):删除属性
print hasattr(zach, "age") #如果存在属性“age”, 返回True
print getattr(zach, "height") #返回属性"height"的值
print setattr(zach, "age", 37) #设置属性"age"的值为37
print delattr(zach, "age") #删除属性"age"
类的内置属性
- doc:类的文档字符串
- name:类名
- bases:类的所有父类构成元素(包含了一个由所有父类组成的元组)
- module:类定义所在的模块(类的全名是’main.className’, 如果类位于一个导入模块mymod中,那么className.module等于mymod)
- dict:类的属性(包含一个字典,由类的数据属性组成)
内置类属性调用实例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Person:
"所有人的基类"
number = 0;
def __init__(self, name, age):
self.name = name
self.age = age
Person.number += 1
def showName(self): #showName
print "my name is %s" % self.name
def showNum():
print "Total Person %d" % Person.number
print "Person.__doc__:", Person.__doc__
print "Person.__name__:", Person.__name__
print "Person.__module__:", Person.__module__
print "Person.__bases__:", Person.__bases__
print "Person.__dict__:", Person.__dict__
执行以上代码输出结果为:
Person.__doc__: 所有人的基类
Person.__name__: Person
Person.__module__: __main__
Person.__bases__: ()
Person.__dict__: {'__module__': '__main__', 'showNum': <function showNum at 0x7fb8bd676758>, 'number': 0, '__init__': <function __init__ at 0x7fb8bd676668>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe4\xba\xba\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', 'showName': <function showName at 0x7fb8bd6766e0>}
python对象销毁(垃圾回收)
Python使用了引用计数这一简单技术来追踪内存中的对象。在Python内部记录着所有使用中的对象各有多少次引用。当对象被创建时,就建立了一个引用计数器,当这个对象不再需要时,也就是说,这个对象的引用计数变为0时,它被垃圾回收。但是,回收不是”立即”的,由解释器在适当的时候,将垃圾对象占用的内存空间回收。
a = 40 #创建对象<40>
b = a #增加引用<40>的计数
c = [b] #增加引用<40>的计数
del a #减少引用<40>的计数
b = 100 #减少引用<40>的计数
c[0] = -1 #减少引用<40>的计数
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。在这种情况下,仅使用引用计数是不够的,python的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充,垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些情况)的对象。在这种情况下,解释器会暂停下来,试图清理所有未引用的循环。
实例:
析构函数del在对象销毁时候被调用,当对象不再被使用时,del方法运行:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "销毁"
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) #打印对象id
del pt1
del pt2
del pt3
以上运行结构:
140093201611880 140093201611880 140093201611880
Point 销毁
类的特性与普通函数的区别
类的特性,也叫做类的成员函数(其实质是与类实例捆绑的函数)。所以,成员函数可以与类对象(实例)捆绑,也可以与普通函数进行捆绑。
- 成员函数可以绑定到普通函数上。
- 普通函数也可以绑定到类成员函数上。
- 从外部访问类实例的成员函数。
- 禁止外部访问类实例的成员函数:使类成员私有化(私有化的特性只限于类内部使用),在特性的名字前加上单下划线或者双下划线即可。
#!/usr/bin/env python
class Person:
def getName(self, name):
self.name = name;
def showName(self):
print "my name is %s" % self.name
def greeting(self):
print "hello, good morning, I'm %s" % self.name
def habit(self):
print "I like red"
def __secretive(self): #prohibit external access
print "I love Angle"
def showSecretive(self):#inter access "_secretive"
self.__secretive()
def greenHabit():
print "I like green"
zach = Person()
zach.getName("zach")
zach.showName()
zach.greeting()
zach.habit() #access from external
greenHabit()
greenHabit = zach.habit #common function binding member function
greenHabit()
zach.name = "angle" #access from external
zach.showName()
#zach.__secretive()
zach.showSecretive()
结果如下:
类的命名空间
- 所有位于class语句中的代码都在特殊的命名空间中执行。
- 类变量与实例变量的区别:类变量,属于所有实例共享;实例变量,属于个体实例私有。
#!/usr/bin/env python
class MemberCounter:
members = 0 #class variable, all instances share
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
print "MemberCounter-->%d" % MemberCounter.members
print "m1-->%d" % m1.members
#instance m1 apply for a new variable , and overwrite class "members"
m1.members = "one"
print "m1-->%s" % m1.members
m2 = MemberCounter()
m2.init()
m2.init()
print "MemberCounter-->%d" % MemberCounter.members
print "m2-->%d" % m2.members
超类和多个超类
超类是被子类继承的类,当一个子类继承了多个类时,则这个子类含有多个超类。
- 当一个类被子类继承时,则子类自然享有超类的所有特性。
- 当一个子类继承自多个超类,则子类自然享有多个超类的所有特性。
- 当子类重定义与超类相同的特性时,会自动覆盖超类的定义。
下面以“语音计算器”为例进行说明,“语音计算机”继承自“语音”和“计算器”,自然享有“语音”的说话功能和“计算器的”计算功能。
#!/usr/bin/env python
class Calculator:
def calculate(self, expression):
self.value = eval(expression)
class Talker:
def talk(self):
print "Hi, my value is %d" % self.value
class TalkingCalculator(Calculator, Talker):
def talk(self):
print "I am a son, my value is %d" % self.value
tc = TalkingCalculator()
tc.calculate("1 + 2 * 3")
tc.talk()
- 检查一个类是另一个类的子类。使用issubclass,如果是,会返回true
- 检查一个对象是不是一个类的实例。使用isinstance, 如果是,会返回true
print issubclass(TalkingCalculator, Calculator)
print isinstance(tc, TalkingCalculator)
print isinstance(tc, Talker)
基础重载方法
- init(self[,args…]):构造函数,简单的调用方法:obj=className(args)
- del(self):析构方法,删除一个对象,简单的调用方法
- repr(self):转化为供解释器读取的形式,简单的调用方法:repr(obj)
- str(self):用于将值转化为适合人阅读的形式,简单的调用方法:str(obj)
- cmp(self, x):对象比较,简单的调用方法:cmp(obj, x)
运算符重载
Python同样支持运算符重载,实例如下:
#!/usr/bin/env 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
以上代码执行结构如下:
Vector (7, 8)
使用未绑定的类方法与super函数
- 未绑定的类方法:使用类名引用的方法,并在参数列表中引入待绑定的对象(self),从而达到调用父类的目的。
但是存在如下几个方面的缺点:
- 缺点一:当一个子类的父类发生变化时(例如SongBird<–animal),必须定义整个类的定义把所有通过非绑定的方法的类名全部替换过来。如果代码量少的话还可以接受;如果代码量多的话,则是一个灾难。
- 缺点二:当出现多父类时,父类又有公共父类时,公共父类初始化函数会被执行多次。
以下是实例代码:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Bird:
def __init__(self):
print "I am a bird"
class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
print "I am a SongBird"
class DanceBird(Bird):
def __init__(self):
Bird.__init__(self)
print "I am a DanceBird"
class SongDanceBird(SongBird, DanceBird):
def __init__(self):
SongBird.__init__(self)
DanceBird.__init__(self)
print "I am a SongDanceBird"
sdb = SongDanceBird()
运行结果为:
I am a bird
I am a SongBird
I am a bird
I am a DanceBird
I am a SongDanceBird
I am a bird
I am a SongBird
I am a bird
I am a DanceBird
I am a SongDanceBird
类Bird的init()被多次重复执行。
- 利用super函数调用父类的方法
- super函数并不是一个函数,而是一个类名。形如super(SongBird, self),事实上调用了super类的初始化函数,产生了一个super对象。
- super(SongBird, self).func的调用并不是用于调用当前类的父类的func函数
- 混用super类和非绑定的函数是一个非常危险的行为。
- super能阻止对父类方法的多次调用。
以下是实例代码:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
__metaclass__ = type
class Bird:
def __init__(self):
print "I am a bird"
class SongBird(Bird):
def __init__(self):
super(SongBird, self).__init__()
print "I am a SongBird"
class DanceBird(Bird):
def __init__(self):
super(DanceBird, self).__init__()
print "I am a DanceBird"
class SongDanceBird(SongBird, DanceBird):
def __init__(self):
super(SongDanceBird, self).__init__()
print "I am a SongDanceBird"
sdb = SongDanceBird()
执行结果如下:
I am a bird
I am a DanceBird
I am a SongBird
I am a SongDanceBird
类Bird的init()执行一次。