>几乎任何高级编程语言,均支持面向对象。面向对象属于一种思想,需要程序员为此持续锻炼,巩固。
>Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的
这里详细介绍Python的面向对象编程,如果以前没有接触过面向对象的编程语言,
首先要了解下面向对象的一些基本特征:面向对象技术简介
1、概念定义
- 类(Class): 用来描述具有相同的属性和方法的对象集合。它定义了该集合中每个对象所共有的属性和方法。
- 对象(object):通过类定义的数据结构实例。对象包括两个数据成员==属性和方法
- 实例化:创建一个类的实例,类的具体对象体现,将类通过构造函数转化为实例的过程
2、类的成员
- 属性:类变量或者实例变量,用于处理类及其实例对象的相关的数据,比如字符串、数组、字典等
- 类变量:它定义在类中且在方法之外,通常不作为实例变量使用。类变量在整个实例化的对象中是公用的。
- 实例变量:它定义在方法中指令序列的变量,作用于当前实例。每个实例都有自己的实例变量
- 方法:类中定义的函数体,即指令序列,往往可以操作实例变量或者完成某个特定的功能
- 形参:方法名使用的参数,特殊的是self形参,是指类实例指针,构造方法会初始化self,用来指示对象的内存地址
- 实参:方法体使用的参数,是指令序列可以实际操作和修改的参数,一般用[self.实参]表示
>理解self:调用类的构造方法,会初始化self为类实例的对象标识,所以一般也称它为 ==> 类实例指针
3、类的创建
使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体
例子理解#!/usr/bin/python
# -*- coding: UTF-8 -*-
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 1)empCount 是一个类变量,它的值将在类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问
2)第一种方法__init__()方法是类的构造函数或实例化方法,当类被实例化为对象时,默认就会调用该方法
3)self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
4)构造函数的变量称为局部变量,self.Name才是实例变量
4、类实例化
类的实例化方法:init()
实例化方法:实例化时调用[类()],称为类的构造方法,会初始化[self]为实例标识,然后再调用__init__()初始化方法
初始化方法:执行初始化方法,它的形参也是构造方法的形参,形参传递给实例变量 ==> self.valume形式
>核心:实例化方法用来初始化实例变量
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖override
也称为方法的重写。即:需要从子类中重新改写方法。如果要调用父亲同名的方法,使用super()
比如:class Father:
def bor(self):
pass
class Son(Father):
def bor(self #重写)
super(Son, self).bor() #调用父类方法其一
Father.bor(self, ...) #调用父类方法其二
pass 5、类的三大特征
封装:将事物共有的属性或者共有的方法,集成到类里,可以实例化为不同的对象,就称为封装
>封装有点类似函数,可以将共同的属性和方法集成一次,以对象标识区分不同的对象,而求同存异
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。可以调用基类的方法 >继承也允许把一个派生类的对象作为一个基类对象对待。
多继承:默认的情况下,所有的面向对象编程中,一个派生类只能继承一个基类, >但是再C++和Python中,支持一个派生类继承多个基类;
这个时候基类中相同的方法,派生类会按照继承顺序,依次使用方法;
但是,如果两个继承基类,又继承同一个根类,那么根类中的方法,最后才执行
多态:Python原生多态,即变量不区分类型 >======================面向对象中高级进阶==============================
6、面向对象的高级知识
a、类的属性字段
1)类的帮助信息可以通过ClassName.__doc__属性查看
2)class_suite 类成员是由,类中的方法,数据属性组成
(其一)数据属性:也叫做字段,或者叫变量 实例变量(普通字段)
>==> 实例化方法中初始化的变量,称为实例变量,作用域是对象中,一般形式时self.valume
访问方式:必须初始化为对象,通过[对象名.实例变量],定义在self后面
形式变量 >==> 方法名(包括构造方法)的参数列表中的变量,称为形式变量,或者形参,只给编译器使用
访问方式:无(执行对象方法时,编译器会将传递的参数给实例变量)
类变量(静态字段) >==> 类方法中初始化的变量,称为类变量,在所有实例化对象之间共享,保存再类中
访问方式:[类名.类变量名](也可以通过对象访问,[对象名.类变量名])
b、类的实例化和方法
实例化类其他编程语言中一般用关键字 new,但是在 Python 中类的实例化类似函数调用方式
>以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接受参数
>类方法__init__() 是类的构造函数或初始化方法,类被实例化时就会调用这个方法1)"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
2)"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000) 注意:访问属性
1)您可以使用点(.)来访问对象的属性和方法。使用如下类的名称访问类变量:emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount。 2)你也可以使用以下内嵌函数的方式来访问属性:getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) : 检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。 注意:执行动作
很清楚:类是由数据成员和方法构成的;数据成员即类属性:方法是类中定义的动作
(其二)方法:也叫做类中的函数 类的实例化方法:init()
>实例化方法:实例化类用[类()],将初始化[self]为实例标识(指针)和实例变量
内部先调用__new__()构造方法,然后调用__init__()初始化方法,传递形参
实例方法: >使用 def 关键字定义在类内部,以self为首形参,并通过实例对象调用的方法。==> 称为实例方法
实例方法必须包含参数self,调用有通过[对象名.方法名]执行,往往集成类的实现功能
类方法: >使用 def 关键字定义在类内部,以cls为首形参,并通过类直接调用的方法。 ==> 称为类方法
类方法前加:装饰器 @classmethod ,必须包含参数cls,调用通过[类名.类方法]执行
类的静态方法: >使用 def 关键字定义在类内部,不以self和cls为参数,并通过类直接调用的方法。 ==> 称为静态方法
静态方法前加:装饰器 @staticmethod ,不使用self、cls作为参数,属于类,调用通过[类名.静态方法]执行
不伦不类的获取属性: >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器 @property ==> 用户获取方法值
特殊属性包含参数self,通过对象名.方法名调用,但不需要括号了
不伦不类的设置属性: >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器 @获取方法.setter ==> 用于设置
方法值, 特殊属性包含参数 self,通过[对象名.方法名]=value调用
不伦不类的删除属性: >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器 @获取方法.delete ==> 用于删除
>方法, 特殊属性包含参数 self,通过[del 对象名.方法名]调用,不是真正的删除,只是一种调用方式
然后:==================
很明显:@property 装饰器 ==> 把类中动态方法的访问形式,转化为动态字段的访问方式比如:调用 emp1.displayEmployee()动态方法调用,可以转化为 emp1.displayEmployee 动态字段调用
还有另一个写法:def bor(): property(fget=bor,fset=xxx, fdel=zzz) ,property方法三个参数,和上面是一样的 c、类的成员修饰符
公有字段 == 能够在类内定义,类外访问的属性或变量
>定义:self.value = 'liujian' ; 访问:print(obj.value)
默认定义的实例变量、类变量等字段,都是公有字段
私有字段 == 在类内定义,类内访问,类外不可以访问的属性或变量 >__private_attrs:两个下划线开头,声明该属性为类的私有字段,不能在类的外部被使用或直接访问。
在类内部通过定义方法,使用时return self.__private_attrs。
公有方法 == 能够在类内定义,类外执行的方法或者函数 >定义:def func(): pass ; 执行: obj.func()
默认定义的动态方法、静态方法等,都是公有方法
私有方法 == 在类内定义、类内调用,类外不可以调用执行的方法 >__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用执行。
在类的内部调用 self.__private_methods
注意:私有属性 ==> 是指,仅仅能够在定义的类中,访问或者调用的属性,不能继承Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName 访问属性,
将如下代码替换以上代码的最后一行代码:
.........................
print counter._JustCounter__secretCount 注意:通常你需要在单独的文件中定义一个类
>类名()==》 调用__init__()方法
>对象名()==》 调用__call__()方法
特殊的内部方法:def __init__(self): 构造方法,当类被初始化为对象时,自动执行
def __call__(self): 特殊方法,当对象以方法的形式调用时,自动执行,比如obj()
def __int__(self): 数值转化方法,直接打印,将对象转化为数字
def __str__(self): 字符串转化方法,直接打印,将对象转化为字符串
def __del__(self): 析构方法,在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行
def __add__(self): 对象之间操作,默认会执行第一个对象obj1.__add__()方法,并传递其他对象为参数other
当两个对象相加时,默认执行第一个对象的__add__()方法,并将第二个对象作为参数传递进去 ==> 在python中,每种类型都是一个类,这种类型的操作支持,都是都是因为类中存在很多特殊方法 对应方法如下:
>def __getitem__(self, itme) 切片或者索引 ; def __set__(self, key, value) ; def __delitem__(self, value)
另外一个重要的特殊修饰符:
>def __iter__(self) ==> 执行对象的iter方法,获取一个迭代器对象供给for循环迭代(迭代器是另一个类)
注意:Python内置类属性__dict__ : 类的自身属性(这是一个字典,由类的数据属性和结构组成)
__doc__ :类的文档字符串(主要对类进行说明,或者作者简介)
__name__: 类名,本类的名称
__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
单下划线、双下划线、头尾双下划线说明:
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的 类在实例化时,可以传递变量参数;如果类在实例化后,也可给对象赋值
1)默认情况里,对象被实例化后,不可以修改,也就是只读,但是有个前提:继承object基类
2)装饰器 @function_name.setter ,表示对象没实例化后,可写
注意:self代表类的实例,而非类
类的方法与Py函数有一个特别的区别——类方法必须有一个额外的第一个参数名称, 按照惯例它的名称是 selfclass Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt() 从执行结果可以很明显的看出:self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
d、对象的销毁
又称垃圾回收。Python 使用了引用计数这一简单技术来跟踪和回收垃圾,它内部记录着所有使用中的对象各有多少引用
原理:一个内部跟踪变量,称为一个引用计数器。
>1)当对象被创建时, 就创建了一个引用计数;
2)当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它就会被垃圾回收
但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。
循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。
这种情况下,Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。
作为引用计数的补充,垃圾收集器也会留心被分配的总量很大的对象(及未通过引用计数销毁的那些)。
在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环
析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行
所以一个完整的类被定义为: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, "销毁"
6、类实继承
面向对象的编程带来的主要好处之一是代码重用,因为类往往能封装一些功能,实现这种重用的方法之一是通过继承机制。
继承完全可以理解成类之间的类型和子类型关系。需要注意的地方:
继承语法 :
>class 派生类名(基类名): //... 基类名写在括号里,基类是在类定义的时候,在元组之中指明的
在python中继承中的一些特点:
>1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用
>2:调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别在类中调用普通函数时并不需要带上self参数
>3:Py总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
(先在本类中查找调用的方法,找不到才去基类中找)。
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
语法:派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite 可以使用issubclass()或者isinstance()方法来检测,类是否继承于某个父类。
>issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
>isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
类的继承本质: 面向对象的编程思想,主要体现在类的封装;类 ==> 又称为类型定义,正如str、tune、list、int等
“类型class”继承为“type”类;换句话说《类的继承,本质上是父类的实例化过程》,推理出类型class,也是实例对象
那么:Class A(B) ; 类A,继承 类B ==> 类A,是类B的实例化对象,即A,也是对象;(主要是Type的对象)
此时,解释器立刻执行 Type.__init__(self, *args, **kwargs),目的是初始化对象A,也就是类A
obj = A() ; 实例化类A,生成真正想要的对象obj ==> 就是对象A的方法调用,A() ,此刻导致
类A的父类Type.__call__(self, *args, **kwargs)特殊方法的执行,这里面又会呈现如下调用:
>a) self.__new__(self, *args, **kwargs) 方法, ==》这是什么,这是类B的实例化对象A的__new__()函数
>b) self.__init__(self, *argv, **kwargs) 方法,==》这是什么,这是类B的实例化对象A的__init__()函数 由此可见:
创建对象obj时,通过类A的父类Type.__call__()方法,依次执行了两步:
>1)使用A.__new__()生成一下新对象
>2)使用A.__init__()初始化这个新对象
那么Class A(B)这个步骤,其实也会再次递归到B的继承,B.__new__()方法和B.__init__()方法的执行
7、方法重写
如果父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Parent: # 定义父类
def myMethod(self):
print '调用父类方法'
class Child(Parent): # 定义子类
def myMethod(self):
print '调用子类方法'
c = Child() # 子类实例
c.myMethod() # 子类调用重写方法 列出了一些通用的功能,你可以在自己的类重写:
>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)
运算符重载, Python同样支持运算符重载,实例如下:#!/usr/bin/python
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
|