A Byte of Python 笔记(9) 面向对象编程

时间:2020-11-26 11:50:11

第11章  面向对象编程

面向过程:根据操作数据的函数或语句块来设计程序。

面向对象(OOP, object-oriented programming):把数据和功能结合起来,用对象包裹组织程序。

类是用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。

类和对象是面向对象编程的两个主要方面。类创建一个新类型,而对象是类的 实例。

类似于你有一个 int 类型的变量,存储整数的变量是 int 类的实例(对象)。

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

类的属性有域和方法。域即类的变量,可以存储数据;方法即类的函数,具有某种功能。

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

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

 

self

类的方法与普通函数有一个特别的区别,类方法必须有一个额外的第一个参数名称 self(这个变量指向对象本身),调用时无需为该参数赋值,python 会提供这个值。如果你有一个不需要参数的方法,也需要给这个方法定义一个 self 参数。

Python中的 self 等价于C++中的 self 指针和 Java、C#中的 this 参考。

self 的原理:类 MyClass 的实例 MyObject,调用对象的方法 MyObject.method(arg1, arg2)时,python 会自动转为 MyClass.method(MyObject, arg1, arg2)。

# -*- coding: utf-8 -*-
# Filename: simplestclass.py 

class Person:
    pass

p = Person()
print p

A Byte of Python 笔记(9) 面向对象编程

使用 class 语句后跟类名创建一个新类,类体为一个空白块,由 pass 语句表示。

创建一个对象/实例:使用类名后跟一对圆括号。打印的是存储对象的计算机内存地址。

 

对象的方法

类/对象可以拥有像函数一样的方法,这些方法与函数的区别只是一个额外的 self 变量。

# -*- coding: utf-8 -*-
# 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()
Person().sayHi()

A Byte of Python 笔记(9) 面向对象编程

sayHi 方法没有任何参数,但仍然在函数定义时有 self。

 

__init__() 方法

__init__() 方法是一种特殊的方法,称为类的构造函数或初始化方法,当类的对象/实例创建时就会调用,用来对对象做一些初始化。

注意:__init__ 的开始和结尾都是双下划线。

# -*- coding: utf-8 -*-
# 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()
Person('Swaroop').sayHi()

A Byte of Python 笔记(9) 面向对象编程

__init__ 方法定义为取一个参数 name(以及普通的参数self)。__init__创建了一个新的域,称为 name。

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

__init__ 方法类似于C++、C# 和 Java 中的 constructor 。

 

类与对象的方法

数据是与类和对象的名称空间 绑定 的普通变量,即这些名称只在这些类与对象的前提下有效。

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

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

对象的变量 由类的每个对象/实例拥有。每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是互不相关的。

# -*- coding: utf-8 -*-
# Filename: objvar.py

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

    def __init__(self, name):
        '''Initializes the person's data.'''
        self.name = name
        print '(Initializeing %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()

A Byte of Python 笔记(9) 面向对象编程

本例说明了类与对象的变量的本质。population 属于 Person 类,是类的变量;name 变量属于对象(使用 self 赋值),是对象的变量。

两个特殊方法 __init__  和  __del__ :

(1)__init__() 方法 被称为类的构造函数,用来初始化 Person 实例。self.name 的值根据每个对象指定,表明了它作为对象的变量的本质。

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

(2)__del__() 方法 被称为类的析构函数,在对象消逝时被调用。对象消逝即对象不再被使用,它所占用的内存将返还给系统。当对象不再使用时, __del__ 方法运行,如果很难保证究竟在 什么时候 运行。用户可以指明它的运行,使用 del 语句。

 

类的文档字符串可以通过 ClassName.__doc__ 查看:

运行时使用 Person.__doc__ 和 Person.sayHi.__doc__ 分别访问类与方法的文档字符串。

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

Python对象销毁(垃圾回收)

1. 同Java语言一样,Python使用了引用计数这一简单技术来追踪内存中的对象。
2. 垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。
上述实例中如执行:del u1,则u1对象被销毁,打印:User destroyed

 

Python 内置类属性

__doc__ 类的文档字符串

__name__ 类名

__module__ 类定义所在的模块(类的全名是 '__main__.className',如果类位于一个导入模块 mymod 中,那么 className.__module__=mymod)

__bases__ 类的所有父类构成元素(包含了一个由所有父类组成的元组)

__dict__ 类的属性(包含一个字典,由类的数据属性组成)

 

 

继承

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

1、声明类的时候括号中写要继承的父类

2、类的继承衍生出子类,子类可以继承或重写父类的方法,子类可以自定义新的方法或成员变量。

例:教师和学生有共同属性,也有专有属性。可以创建一个共同的类 SchoolMember ,让教师和学生的类 继承 这个类,即它们都是这个类型(类)的子类型,然后再为这些子类型添加专有的属性。

向 SchoolMember 中增加/改变任何功能,会自动反映到子类型中。在子类型中做改动不会影响到别的子类型。子类型在任何需要父类型的场合可以被替换成父类型,即对象可以被视作父类的实例,这种现象被称为多态现象。

SchoolMember 称为 基本类 或 超类;Teacher 和 Student 类被称为 导出类 或 子类。

# -*- coding: utf-8 -*-
# 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 a Student: %s)' % self.name

    def tell(self):
        SchoolMember.tell(self)
        print 'Marks:"%d"' % self.marks

t = Teacher('Mrs. Shriry',40,30000)
s = Student('Tom',22,75)

print # prints a blank line

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

A Byte of Python 笔记(9) 面向对象编程

为了使用继承,我们把基本类的名称作为一个元组跟在定义类时类名称之后。

基本类的 __init__ 方法专门用 self 变量调用,可以初始化对象的基本类部分

——python 不会自动调用基本类的 constructor,必须专门调用。

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

 

python 首先查找对应类型的方法,如果不能在导出类中找到对应的方法,才开始到基本类中逐个查找。

基本类在类定义的时候,在元组中指明。如果在继承元组中列出了一个以上的类,称为 多重继承。

 

Python类私有属性与方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。

  在类内部的方法中使用时self.__private_attrs。
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。

  在类的内部调用self.__private_methods

 

Python实例方法、类方法、静态方法

实例方法,类方法,静态方法都可以通过实例或者类调用,只不过实例方法通过类调用时需要传递实例的引用(python 3可以传递任意对象,其他版本会报错)
实例方法针对的是实例,第一个参数是self,普通对象方法至少需要一个self参数,代表类对象实例;

类方法针对的是类,@classmethod 它表示接下来的是一个类方法,类方法的第一个参数cls,它们都可以继承和重新定义;

静态方法用于作为程序中的共享资源,直接通过类去调用,不用实例化对象,不需要self参数,可以认为是全局函数,@staticmethod 它表示接下来的是一个静态方法