python是一种面向对象的语言。本文介绍如何用python进行面向对象的编程。

首先我们说下对象和类,类是一种类型的定义,对象是类的实例。

一、内置对象类型(内置类)

其实我们前面已经大量用到了对象,如字符串、列表、字典等,这些对象的类型是python的内建对象类型。

比如:

a=[]

这其实就是创建了一个空的列表对象,并将它赋值给变量a,变量a就指向了一个列表对象。

那列表对象对应的类是什么呢?其实列表对象对应的类名是list。

我们还可以通过类来创建对象,看下例子:

>>> print list
<type 'list'>
>>> a=list()
>>> print a
[]

看以看出 print list 显示list是中类型,而不是报错,如果我们随便 list xxx ,除非已经将xxx声明为变量,肯定会报错。
也可以看出 a=list() 创建了一个空的列表对象。

事实上,通过类创建对象的语法就是  类名()  , 根据类的定义不同,()中可能需要传递参数。

我们再看几个内置对象的创建。

>>> a=tuple()
>>> print a
()
>>> print tuple
<type 'tuple'>
>>> a=dict()
>>> print a
{}
>>> print dict
<type 'dict'>
>>> a=str('hello')
>>> print a
hello
>>> print str
<type 'str'>

可以看出 元组对象的类名为 tuple ,字典对象的类名为dict, 字符串对象的类名为str

二、自定义类

面向对象的编程,不能只使用内置类型,关键是能自己定义类和创建自定义类的对象。

在python中,通过关键字 class来定义类,我们还是通过举例代码来了解。

1、定义类

举例代码如下

class Dog:
def setColor(self,color):
self.color=color
def getColor(self):
return self.color dog = Dog()
dog.setColor("red")
print dog.getColor()
print dog.color

上面代码,我们定义了一个Dog类,该类中有2个方法(函数在类中一般称为方法)。
然后我们创建了Dog类的一个对象,并声明了一个全局变量dog,dog变量指向创建的对象。

代码执行的结果输出为:

red

red

看到这个Dog类的定义代码,熟悉java,c#等语言的人可能有些迷惑了? 对比下java的代码(c#也类似)。

class Dog{
public String color;
public void setColor(String color) {
this.color = color;
}
public String getColor(){
return this.color;
}
}

上面是java中的对应的Dog类的定义,与python中的定义有较大差异。但调用方式和结果没有区别。

第一个差异是,python中的两个方法的第一个参数都是一个self参数,但在调用时却没有给self参数传值。

在python中,对于类中的方法,与普通的函数相比,有个核心的区别是,方法的第一个参数有着特殊的含义,这也意味着定义方法时至少要定义一个参数。

但是当我们通过对象调用方法时,却不需要给该参数传值,该参数值python解释器来负责传入,代表对象本身。就如同java中的关键字this。

我们例子中取得参数名是 self,注意self并不是关键字,实际上我们可以取任何的合法标识符,只不过因为该参数代表对象本身,self名字上比较贴近。

在方法中引用类中定义的变量和方法,需要通过参数self来引用。

第二个差异是,color这个属性(变量在类中习惯被称为属性)没有在类中定义,在setColor方法中通过 self.color=color 语句就为对象添加了一个属性并给予初始化赋值

这个是动态语言的特点,动态语言的对象可以在运行期绑定属性和函数。这在后面有详细介绍。

上面的代码在setColor方法中为对象添加了color属性,存在的一个问题是,如果在没有调用setColor方法之前,就通过对象引用该属性,比如调用getColor或dog.color,则会报错,因为color还不存在。 这就需要通过类的构造函数来解决。见下面的介绍。

2、构造方法

所谓构造方法(其它语言中喜欢叫构造函数),是类中一个特殊的方法,当对象被创建时,会自动被调用,可以在该方法中进行一些对象属性的初始化工作。

既然是自动被调用,其方法名肯定不能和其它普通方法一样,其方法名是随意的,需要约定的。在其它语言中,构造方法的名称一般同类名一样,而在python中,构造方法的名字要求是 __init__ ,注意init前后是连个连续的下划线。

我们来给上面的Dog类增加一个构造方法,其它代码不变,代码如下:

class Dog:
def __init__(self,color="black"):
self.color=color
def setColor(self,color):
self.color=color
def getColor(self):
return self.color dog = Dog()
print dog.color
print dog.getColor() dog.setColor("red")
print dog.color
print dog.getColor()

可以看出,构造方法__init__有两个参数,一个是强制的self,另一个是color,并且color给了个默认值。
因为构造方法的参数color有默认值,这样创建对象时可以不传参数。 如果没有默认值,就不行,创建对象必须要传入参数。

有了构造方法,当对象创建后,对象就拥有了color属性,后面就可以随便通过self来引用了。

3、私有成员

在java等语言中,支持私有成员,私有成员在类的外部不能访问,这增加了类的安全性。

在python中,语言底层是不支持私有成员的,但我们可以给 成员的名字前加 __ ,两个连续的下划线,这样这种命名的成员就不能在类的外部访问了。

举例如下:

class Dog:
def __init__(self,color="black"):
self.__color=color
def __eat(self):
print "a am eat"
def getColor(self):
return self.__color
def setColor(self,color):
self.__color=color
self.__eat()

上面的代码中,我们在color和eat的前面加了__,这样color属性和 eat方法就变成私有成员了,在类的内部可访问,但外部不可访问。

4、类的继承

面向对象编程的一个重要特点就是类的继承,python支持子类可以同时继承多个父类。但是多重继承容易降低代码的可读性和可维护性,正常情况下我们不推荐使用。类的继承语法很简单,声明的格式是 : class 子类名 (父类名) ,如果有多个父类,()中就可以写多个父类,之间以逗号分隔。

举个例子:

class A:
def hello(self):
print "A" class B(A):
pass b = B();
b.hello();

可以看出,类B继承了类A,这样B的对象可以直接使用A中定义的方法。
我们再看下如果父类有构造方法,子类该怎么定义和使用?

class A:
def __init__(self,msg):
self.msg = msg
def hello(self):
print self.msg class B(A):
pass b = B("hello");
b.hello();

如果子类没有自己特殊要求,就不需要定义自己的的构造方法,则创建子类对象时,直接使用父类的构造方法即可。

我们再看下父类有构造函数,子类也需要自己的构造函数(完成自己的初始化需求),这个怎么实现?

class A:
def __init__(self,msg):
self.msg = msg
def hello(self):
print self.msg class B(A):
def __init__(self,othermsg):
self.othermsg = othermsg
def show(self):
print self.othermsg b = B("hello");
b.show();
b.hello(); #会报异常

上面代码子类定义了自己的构造方法。执行上述代码,到最后一句 b.hello()会报异常,提示A类中的msg未定义。
这是因为A 类的构造函数没有被执行到。我们需要修改代码如下:

#coding=utf-8
__metaclass__ = type
class A:
def __init__(self,msg):
self.msg = msg
def hello(self):
print self.msg class B(A):
def __init__(self,othermsg,msg):
super(B,self).__init__(msg);
self.othermsg = othermsg
def show(self):
print self.othermsg b = B("hello","good");
b.show();
b.hello();

类A的代码没变,类B增加了一个构造方法,它即需要初始化自己的属性,又需要初始化父类的属性,所有除self外,还有2个参数。
关键是这里用了super这个内置方法,来调用父类的构造方法。

还有一点需要特别注意的是,要想super语句能通过,必须在文件的开头加上  __metaclass__ = type 这个语句。

原因是因为python自身版本的问题,其类的实现机制分为旧式类和新式类,在python3.0中只有新式类了。

新式类的使用中有些新的更好的特性,比如super方法是新式类中的特性。

我们要想在python2.x版本中使用新式类,就必须在文件的开头加上  __metaclass__ = type 这个语句。

正常情况下,我们都应该使用新式类,一来可以使用新特性,二来为了方便将来向python3.0过渡。

5、方法的重载

在python中,除构造方法外,并不支持方法重载功能(即一个类中允许存在方法名相同,参数不同的多个方法,调用时根据传入的参数来区别)。如:

#coding=utf-8
__metaclass__ = type
class A:
def hello(self):
print "default"
def hello(self,msg):
print msg

上面定义的类,如果我们这么调用
a = A()
a.hello()

这时当执行 a.hello() 语句就会报错,提示没有参数。而如果调用 a.hello("test") 就能成功

这是因为python中,不支持方法重载,可以定义同名方法,但后面的会覆盖前面的。

比如上面的代码,如果我们两个hello方法,换个位置的话, a.hello()就可以运行,但 a.hello("test") 就报错了。

如果要想 a.hello() 和   a.hello("test") 都能通过,则可以通过参数缺省值的方式来实现。如:

#coding=utf-8
__metaclass__ = type
class A:
def hello(self,msg="default"):
print msg a = A()
a.hello()
a.hello("good")

上面代码执行就没有问题,执行后输出为
default
good

6、方法的重写

子类重写父类的方法,是面向对象的重要特性多态的体现。python显然也支持。举例说明:

#coding=utf-8
__metaclass__ = type
class A:
def hello(self):
print "i am A" class B(A):
def hello(self):
print "i am B" a = A()
a.hello()
b = B()
b.hello()

运行上面代码,输出:
i am A
i am B

因为python作为动态语言,变量定义不需要声明类型,所以也不存在父类的变量指向子类的实例的说法了。

7、 类的静态成员

对于一个类来说,其成员可以分成两类,一类是实例成员,一类是类成员(也称为静态成员)。

实例成员是被每个对象拥有,静态成员被类拥有。

在python中,可以用@staticmethod修饰符来标记一个方法是静态方法。举例如:

#coding=utf-8
__metaclass__ = type class A:
@staticmethod
def fun1():
pass
def fun2(self):
pass a = A()
a.fun1()
a.fun2() A.fun1()
A.fun2()

上述代码最后一个语句 A.fun2() 会报错,因为fun2是实例方法,无法被类直接调用,需要被类的实例(对象)调用。
我们可以在实例方法中通过 self 或 类名 来调用静态方法。但在静态方法中却不能调用实例方法,因为静态方法中无法获取到对象本身,除非我们自己给它加参数。

上面介绍的是静态方法,下面我们看下python类中的静态属性。我们最前面例子中的color属性是实例属性。

直接在类中定义的变量是静态属性,在方法中(包括构造方法中)通过 self.变量名 定义的是实例属性。如:

#coding=utf-8
__metaclass__ = type class A:
name="class" def fun(self,name):
self.name= name; a = A()
print a.name
print A.name a.fun("hello")
print a.name
print A.name A.name = "newclass"
print a.name
print A.name

运行上面代码,看输出,就可以理解静态变量和实例变量的区别。

8、对象的动态特点

在java,c++等非动态语言中,我们根据一个类创建了一个对象,这个对象拥有的成员(属性和方法)是固定不变的,就是类中定义的,对于属性,区别只是不同对象有不同的值罢了。

而在python这种动态语言中,创建的对象,还可以动态的绑定属性和方法。

其实从上面的一些例子代码中也可以看出这点,我们在方法中 通过 self.变量名 就可以产生一个对象(实例)变量,而且这个定义可以在任意实例方法中

我们再举个例子。

#coding=utf-8
__metaclass__ = type def fun(info):
print info class A:
pass a= A()
a.msg="dd" # 动态绑定属性
a.test = fun #动态绑定方法 print a.msg
a.test("good")

我们也可以将对象自己传给绑定的方法,让绑定的方法可以访问对象中的属性和其它方法,如:

#coding=utf-8
__metaclass__ = type def fun(self,info):
print self.msg +","+info class A:
pass a= A()
a.msg="dd" # 动态绑定属性
a.test = fun #动态绑定方法 print a.msg
a.test(a,"good")

这个允许对象可以动态绑定成员的特性是动态语言的一个重要特点。这个特性,有时还是很有用的。
当然,对象动态绑定只影响对象自己,不影响它所关联的类,和其它同一个类创建的对象。

Python 2.7 学习笔记 面向对象的编程的更多相关文章

  1. python学习笔记--面向对象的编程和类

    一.面向对象的编程 面向对象程序设计--Object Oriented Programming,简称oop,是一种程序设计思想.二.面向对象的特性类:class类,对比现实世界来说就是一个种类,一个模 ...

  2. python 3.x 学习笔记13 (网络编程socket)

    1.协议http.smtp.dns.ftp.ssh.snmp.icmp.dhcp....等具体自查 2.OSI七层应用.表示.会话.传输.网络.数据链路.物理 3.socket: 对所有上层协议的封装 ...

  3. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  4. C#学习笔记——面向对象、面向组件以及类型基础

    C#学习笔记——面向对象.面向组件以及类型基础 目录 一 面向对象与面向组件 二 基元类型与 new 操作 三 值类型与引用类型 四 类型转换 五 相等性与同一性 六 对象哈希码 一 面向对象与面向组 ...

  5. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  6. 0025 Java学习笔记-面向对象-final修饰符、不可变类

    final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...

  7. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  8. 孙鑫VC学习笔记:多线程编程

    孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified ...

  9. Requests:Python HTTP Module学习笔记(一)(转)

    Requests:Python HTTP Module学习笔记(一) 在学习用python写爬虫的时候用到了Requests这个Http网络库,这个库简单好用并且功能强大,完全可以代替python的标 ...

随机推荐

  1. 转:alphaImageLoader滤镜加载后 链接不能点击

    我是一个很少使用IE滤镜,也是一个不赞成使用IE滤镜的前端工程师.不过今天有一个朋友给我发来了一个有关于IE6的BUG,就是在IE6中使用了AlphaPNG透明的IE滤镜之后,a链接不能够点击.具体情 ...

  2. protel99se中做拼板图解

    很多时候我们要在protel99se中做拼板, 但是通常在复制进行拼版的时候会出现如下的效果,元件被重新命名了. 而无法达到我们需要的像下图的效果 那我们怎么办,才能达到上图的效果呢?其实操作很简单. ...

  3. qt数据库多线程问题的解决(QSqlDatabase只能在创建它的线程中使用)

    Qt数据库由QSqlDatabase::addDatabase()生成的QSqlDatabase只能在创建它的线程中使用, 在多线程中共用连接或者在另外一个线程中创建query都是不支持的几乎国内没有 ...

  4. C#语言基础之运算符

    运算符分类.优先级 运算符:一.数学运算符:+,-,*,/,++,-- 示例1: 示例2: 示例3: 1.递增运算符:++(1)前缀递增运算符    int x=4;    x++;//输出结果,x的 ...

  5. 【转】CTE(公用表表达式)

    本文转自:爽朗的微笑  http://www.cnblogs.com/shuangnet/archive/2013/03/22/2975929.html 公用表表达式 (CTE) 具有一个重要的优点, ...

  6. Oracle递归sql笔记

    查询一个机构下所辖机构: select * from t00_organ t start with t.organkey=#uporgankey# connect by prior t.organke ...

  7. C#高级编程零散知识点

    1.206-实现单链表的添加和插入 207-实现单链表的其他功能和 3.209-Lambda表达式 4.301-栈的介绍和BCL中的栈 4.501-进程和线程的概念[00_12_06][2015122 ...

  8. Reverse Integer - Palindrome Number - 简单模拟

    第一个题目是将整数进行反转,这个题实现反转并不难,主要关键点在于如何进行溢出判断.溢出判断再上一篇字符串转整数中已有介绍,本题采用其中的第三种方法,将数字转为字符串,使用字符串比较大小的方法进行比较. ...

  9. JS拖动浮动DIV

    <!DOCTYPE html> <html> <head> <meta charset="utf8"> <title>j ...

  10. poj 2992

    http://poj.org/problem?id=2992 大意:求(n,k)的因子个数 解题思路:(n,k) = n!/(k!(n-k)!)  任意一个数都可以用其质因子来表示  eg: 26 = ...