简明Python教程学习笔记_6_面向对象编程

简介

类和对象是面向对象编程的两个主要方面。创建一个新类型,而对象这个类的 实例 。这类似于你有一个int类型的变量,这存储整数的变量是int类的实例(对象)。

类(Class)::用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
对象:          通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
实例化:      创建一个类的实例,类的具体对象。
方法:          类中定义的函数。
数据成员:  类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重载:  如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),重载。
实例变量:  定义在方法中的变量,只作用于当前实例的类。
类变量:      类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

继承    :即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。


给C/C++/Java/C#程序员的注释
注意,即便是整数也被作为对象(属于int类)。这和C++、Java(1.5版之前)把整数纯粹作为类型是不同的。通过help(int)了解更多这个类的详情。 C#和Java 1.5程序员会熟悉这个概念,因为它类似与封装与解封装 的概念。

对象可以使用普通的 属于 对象的变量存储数据。属于一个对象或类的变量被称为。对象也可以使用 属于 类的函数来具有功能。这样的函数被称为类的方法。这些术语帮助我们把它们与孤立的函数和变量区分开来。域和方法可以合称为类的属性

域有两种类型——属于每个实例/类的对象或属于类本身。它们分别被称为实例变量类变量

类使用class关键字创建。类的域和方法被列在一个缩进块中。


在Python类中定义的方法通常有三种:实例方法、类方法、静态方法
这三者之间的区别是:

实例方法:一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,
类方法    :以cls作为第一个参数,cls表示类本身,定义时使用@classmethod,那么通过cls引用的必定是类对象的属性和方法;
静态方法:不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用@staticmethod,静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。


而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析)

不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。


可以直接在类外通过对象名访问,如果想定义成私有的,则需在前面加2个下划线 ' __'
构造方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法.
构造方法支持重载,如果用户自己没有重新定义构造方法,系统就自动执行默认的构造方法。
析构方法__del__(self)在释放对象时调用,支持重载,可以在里面进行一些释放资源的操作,不需要显示调用。


创建实例对象
要创建一个类的实例,你可以使用类的名称,并通过__init__方法接受参数。
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
访问属性
使用点(.)来访问对象的属性。使用如下类的名称访问类变量:
emp1.displayEmployee()
你可以添加,删除,修改类的属性,如下所示:
emp1.age = 7 # 添加一个 'age' 属性
emp1.age = 8 # 修改 'age' 属性
del emp1.age # 删除 'age' 属性
getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) : 检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。
Python内置类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'__main__.className',
如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了以个由所有父类组成的元组)
python对象销毁(垃圾回收)
在Python内部记录着所有使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 
也就是说, 这个对象的引用计数变为0 时,它被垃圾回收。但是回收不是"立即"的, 
由解释器在适当的时机,将垃圾对象占用的内存空间回收。

self

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,但是在调用这个方法的时候你为这个参数赋值,Python会提供这个值。这个特别的变量指对象本身,按照惯例它的名称是self

虽然你可以给这个参数任何名称,但是 强烈建议 你使用self这个名称——其他名称都是不赞成你使用的。使用一个标准的名称有很多优点——你的程序读者可以迅速识别它,如果使用self的话,还有些IDE(集成开发环境)也可以帮助你。

给C++/Java/C#程序员的注释
Python中的self等价于C++中的self指针和Java、C#中的this参考。

你一定很奇怪Python如何给self赋值以及为何你不需要给它赋值。举一个例子会使此变得清晰。假如你有一个类称为MyClass和这个类的一个实例MyObject。当你调用这个对象的方法MyObject.method(arg1, arg2)的时候,这会由Python自动转为MyClass.method(MyObject, arg1, arg2)——这就是self的原理了。

这也意味着如果你有一个不需要参数的方法,你还是得给这个方法定义一个self参数。


创建一个类

#!/usr/bin/python
# Filename: simplestclass.py


class Person:
    pass # An empty block

p = Person()
print p

输出

$ python simplestclass.py
<__main__.Person instance at 0xf6fcb18c>

它如何工作

我们使用class语句后跟类名,创建了一个新的类。这后面跟着一个缩进的语句块形成类体。在这个例子中,我们使用了一个空白块,它由pass语句表示。

接下来,我们使用类名后跟一对圆括号来创建一个对象/实例。(我们将在下面的章节中学习更多的如何创建实例的方法)。为了验证,我们简单地打印了这个变量的类型。它告诉我们我们已经在__main__模块中有了一个Person类的实例。

可以注意到存储对象的计算机内存地址也打印了出来。这个地址在你的计算机上会是另外一个值,因为Python可以在任何空位存储对象。

使用对象的方法

#!/usr/bin/python
# Filename: method.py


class Person:
    def sayHi(self):
        print 'Hello, how are you?'

p = Person()
p.sayHi()


# This short example can also be written as Person().sayHi()

输出

$ python method.py
Hello, how are you?

它如何工作

这里我们看到了self的用法。注意sayHi方法没有任何参数,但仍然在函数定义时有self


__init__方法

在Python的类中有很多方法的名字有特殊的重要意义。现在我们将学习__init__方法的意义。

__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的 初始化 。注意,这个名称的开始和结尾都是双下划线__init__方法

#!/usr/bin/python
# Filename: class_init.py


class Person:
    def __init__(self, name):
        self.name = name

    def sayHi(self):
        print 'Hello, my name is', self.name

p = Person('Swaroop')
p.sayHi()


# This short example can also be written as Person('Swaroop').sayHi()

输出

$ python class_init.py
Hello, my name is Swaroop

它如何工作

这里,我们把__init__方法定义为取一个参数name(以及普通的参数self)。在这个__init__里,我们只是创建一个新的域,也称为name。注意它们是两个不同的变量,尽管它们有相同的名字。点号使我们能够区分它们。

最重要的是,我们没有专门调用__init__方法,只是在创建一个类的新实例的时候,把参数包括在圆括号内跟在类名后面,从而传递给__init__方法。这是这种方法的重要之处。

现在,我们能够在我们的方法中使用self.name域。这在sayHi方法中得到了验证。

给C++/Java/C#程序员的注释
__init__方法类似于C++、C#和Java中的 constructor 。 


类与对象的方法

我们已经讨论了类与对象的功能部分,现在我们来看一下它的数据部分。事实上,它们只是与类和对象的名称空间 绑定 的普通变量,即这些名称只在这些类与对象的前提下有效。

有两种类型的 域 ——类的变量和对象的变量,它们根据是类还是对象 拥有 这个变量而区分。

类的变量 由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,所以当某个对象对类的变量做了改动的时候,这个改动会反映到所有其他的实例上。

对象的变量 由类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是是互不相关的。通过一个例子会使这个易于理解。

使用类与对象的变量

#!/usr/bin/python

# Filename: objvar.py

class Person:
    '''Represents a person.'''
    population = 0

    def __init__(self, name):
        '''Initializes the person's data.'''
        self.name = name
        print '(Initializing %s)' % self.name

        # When this person is created, he/she
        # adds to the population

        Person.population += 1

    def __del__(self):
        '''I am dying.'''
        print '%s says bye.' % self.name

        Person.population -= 1

        if Person.population == 0:
            print 'I am the last one.'
        else:
            print 'There are still %d people left.'% Person.population

    def sayHi(self):
        '''Greeting by the person.

        Really, that's all it does.'''

        print 'Hi, my name is %s.' % self.name

    def howMany(self):
        '''Prints the current population.'''
        if Person.population == 1:
            print 'I am the only person here.'
        else:
            print 'We have %d persons here.'% Person.population

swaroop = Person('Swaroop')
swaroop.sayHi()
swaroop.howMany()

kalam = Person(
'Abdul Kalam')
kalam.sayHi()
kalam.howMany()

swaroop.sayHi()
swaroop.howMany()

输出

$ python objvar.py
(Initializing Swaroop)
Hi, my name is Swaroop.
I am the only person here.
(Initializing Abdul Kalam)
Hi, my name is Abdul Kalam.
We have 2 persons here.
Hi, my name is Swaroop.
We have 2 persons here.
Abdul Kalam says bye.
There are still 1 people left.
Swaroop says bye.
I am the last one.

它如何工作

这是一个很长的例子,但是它有助于说明类与对象的变量的本质。这里,population属于Person类,因此是一个类的变量。name变量属于对象(它使用self赋值)因此是对象的变量。

观察可以发现__init__方法用一个名字来初始化Person实例。在这个方法中,我们让population增加1,这是因为我们增加了一个人。同样可以发现,self.name的值根据每个对象指定,这表明了它作为对象的变量的本质。

记住,你能使用self变量来参考同一个对象的变量和方法。这被称为 属性参考 。

在这个程序中,我们还看到docstring对于类和方法同样有用。我们可以在运行时使用Person.__doc__Person.sayHi.__doc__来分别访问类与方法的文档字符串。

就如同__init__方法一样,还有一个特殊的方法__del__,它在对象消逝的时候被调用。对象消逝即对象不再被使用,它所占用的内存将返回给系统作它用。在这个方法里面,我们只是简单地把Person.population1

当对象不再被使用时,__del__方法运行,但是很难保证这个方法究竟在 什么时候 运行。如果你想要指明它的运行,你就得使用del语句,就如同我们在以前的例子中使用的那样。

给C++/Java/C#程序员的注释
Python中所有的类成员(包括数据成员)都是 公共的 ,所有的方法都是 有效的 。
只有一个例外:如果你使用的数据成员名称以 双下划线前缀 比如__privatevar,Python的名称管理体系会有效地把它作为私有变量。
这样就有一个惯例,如果某个变量只想在类或对象中使用,就应该以单下划线前缀。而其他的名称都将作为公共的,可以被其他类/对象使用。记住这只是一个惯例,并不是Python所要求的(与双下划线前缀不同)。
同样,注意__del__方法与 destructor 的概念类似。 


继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过 继承 机制。继承完全可以理解成类之间的类型和子类型 关系。

假设你想要写一个程序来记录学校之中的教师和学生情况。他们有一些共同属性,比如姓名、年龄和地址。他们也有专有的属性,比如教师的薪水、课程和假期,学生的成绩和学费。

你可以为教师和学生建立两个独立的类来处理它们,但是这样做的话,如果要增加一个新的共有属性,就意味着要在这两个独立的类中都增加这个属性。这很快就会显得不实用。

一个比较好的方法是创建一个共同的类称为SchoolMember然后让教师和学生的类 继承 这个共同的类。即它们都是这个类型(类)的子类型,然后我们再为这些子类型添加专有的属性。

使用这种方法有很多优点。如果我们增加/改变了SchoolMember中的任何功能,它会自动地反映到子类型之中。例如,你要为教师和学生都增加一个新的身份证域,那么你只需简单地把它加到SchoolMember类中。然而,在一个子类型之中做的改动不会影响到别的子类型。另外一个优点是你可以把教师和学生对象都作为SchoolMember对象来使用,这在某些场合特别有用,比如统计学校成员的人数。一个子类型在任何需要父类型的场合可以被替换成父类型,即对象可以被视作是父类的实例,这种现象被称为多态现象

另外,我们会发现在 重用 父类的代码的时候,我们无需在不同的类中重复它。而如果我们使用独立的类的话,我们就不得不这么做了。

在上述的场合中,SchoolMember类被称为 基本类 或 超类 。而TeacherStudent类被称为导出类 或子类 。

现在,我们将学习一个例子程序。

使用继承

#!/usr/bin/python
# Filename: inherit.py


class SchoolMember:
  '''Represents any school member.'''
  def __init__(self, name, age):
    self.name = name
    self.age = age

    print '(Initialized SchoolMember: %s)'% self.name

  def tell(self):
    '''Tell my details.'''
    print 'Name:"%s" Age:"%s"' % (self.name, self.age),

class Teacher(SchoolMember):
  '''Represents a teacher.'''
  def __init__(self, name, age, salary):
    SchoolMember.__init__(self, name, age)
    self.salary = salary

    print '(Initialized Teacher: %s)'% self.name

  def tell(self):
    SchoolMember.tell(self)

    print 'Salary: "%d"' % self.salary

class Student(SchoolMember):
  '''Represents a student.'''
  def __init__(self, name, age, marks):
    SchoolMember.__init__(self, name, age)
    self.marks = marks

    print '(Initialized Student: %s)'% self.name

  def tell(self):
    SchoolMember.tell(self)

    print 'Marks: "%d"' % self.marks

t = Teacher(
'Mrs. Shrividya', 40,30000)
s = Student(
'Swaroop', 22,75)

print # prints a blank line

members = [t, s]
for member in members:
  member.tell()
# works for both Teachers and Students

输出

$ python inherit.py
(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:"Mrs. Shrividya" Age:"40" Salary: "30000"
Name:"Swaroop" Age:"22" Marks: "75"

它如何工作

为了使用继承,我们把基本类的名称作为一个元组跟在定义类时的类名称之后。然后,我们注意到基本类的__init__方法专门使用self变量调用,这样我们就可以初始化对象的基本类部分。这一点十分重要——Python不会自动调用基本类的constructor,你得亲自专门调用它。

我们还观察到我们在方法调用之前加上类名称前缀,然后把self变量及其他参数传递给它。

注意,在我们使用SchoolMember类的tell方法的时候,我们把TeacherStudent的实例仅仅作为SchoolMember的实例。

另外,在这个例子中,我们调用了子类型的tell方法,而不是SchoolMember类的tell方法。可以这样来理解,Python总是首先查找对应类型的方法,在这个例子中就是如此。如果它不能在导出类中找到对应的方法,它才开始到基本类中逐个查找。基本类是在类定义的时候,在元组之中指明的。

一个术语的注释——如果在继承元组中列了一个以上的类,那么它就被称作 多重继承 。


继承语法 :
class 派生类名(基类名)://... 基类名写作括号里,基本类是在类定义的时候,在元组之中指明的。
在python中继承中的一些特点:
1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数
3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
    (先在本类中查找调用的方法,找不到才去基类中找)。   如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。

语法:
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite


#coding=utf-8
#!/usr/bin/python
class Parent: # 定义父类
    parentAttr = 100
    def __init__(self):
        print "调用父类构造函数"
    def parentMethod(self):
        print '调用父类方法'
    def setAttr(self, attr):
        Parent.parentAttr = attr
    def getAttr(self):
        print "父类属性 :", Parent.parentAttr
        
class Child(Parent): # 定义子类
    def __init__(self):
        print "调用子类构造方法"
    def childMethod(self):
        print '调用子类方法 child method'
        
c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法
c.getAttr() # 再次调用父类的方法


以上代码执行结果如下:
调用子类构造方法
调用子类方法 child method
调用父类方法
父类属性 : 200


你可以继承多个类
class A: # 定义类 A
    .....
class B: # 定义类 B
    .....
class C(A, B): # 继承类 A 和 B
    .....
你可以使用issubclass()或者isinstance()方法来检测。
issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
实例:
#coding=utf-8
#!/usr/bin/python
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 )                析构方法, 删除一个对象  简单的调用方法 : dell 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
以上代码执行结果如下所示:
Vector(7,8)


类属性与方法
  类的私有属性
    __private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。
    在类内部的方法中使用时 self.__private_attrs。
  类的方法
    在类地内部,使用def关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数
  类的私有方法
    __private_method: 两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 slef.__private_methods


#coding=utf-8
#!/usr/bin/python
class JustCounter:
    __secretCount = 0 # 私有变量
    publicCount = 0 # 公开变量
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print self.__secretCount
        
counter = JustCounter()
counter.count()
counter.count()
print counter.publicCount
print counter.__secretCount # 报错,实例不能访问私有变量


Python 通过改变名称来包含类名:
1 2 2
Traceback (most recent call last):
File "test.py", line 17, in <module>
print counter.__secretCount # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'
Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName 访问属性,将如下代码替
换以上代码的最后一行代码:
.........................
print counter._JustCounter__secretCount
执行以上代码,执行结果如下:
1 2 2 2




你可能感兴趣的:(简明Python教程学习笔记_6_面向对象编程)