Python科学计算与数据处理7:类和对象

2019-03-23  本文已影响0人  流沙河老船长
class

类与对象

Python是面向对象的语言(object-oriented),同样面向对象的语言还有C++,Java等;与之相对的是面向过程的语言(procedural),例如C语言。前面的教程中,我们也主要是采用了面向过程的编程方式,在这一节中,将为大家介绍面向对象的编程方法,其实现途径就是使用类(class)和对象(object)。

在面向对象的编程中,我们主要处理的不是前面介绍的那样传统的一行行的代码,而是来模拟现实世界中的不同的事物,即对象。一个对象可以有属性和行为,也可以和其他对象相联系和发生相互作用。例如,一只小狗可以有属性(年龄、体重等)和行为(吠、跑、打滚等)。面向对象的编程方式基于以下四个核心特征:

核心概念

这里我们简要介绍一下上述这些重要的概念。

本节的部分内容直接参考了菜鸟教程

创建类

Python中创建类的语法非常简单:

class <class name >(<parent class name>):
'''statements'''
<method definition-1>
<method definition-n>

空类

空类是最简单的类,我们在这里创建一个空类:

class Student():
    '''An empty class.'''
    pass

Student
<class __main__.Student at 0x7f1e7803c668>

上面这个类中没有任何内容,用pass关键字来起到一个占位的作用,Student后面的括号也可以省略,第一行三撇号'''里面的语句为类的文档(可选)。类可以看作是创建实例的蓝图,即使是空类也可以。实际上,空类在我们后面做科研数据处理的过程中有很重要的应用,它可以作为将要分析的数据的一个容器,我们后面会讲到。下面我们就用空类Student来创建实例:

student1 = Student()
student2 = Student()
print student1
print student2
<__main__.Student instance at 0x7fe3507474d0>
<__main__.Student instance at 0x7fe3507472d8>

从上面的输出结果我们看到所创建的两个实例(instance),而且这两个实例在内存中存储在不同的地址上。

实例变量

实例变量是指某个具体实例(对象)特有的变量。例如,针对上面创建的空类的两个实例,我们可以分别赋予它们一些变量:

# define variables for L_obj1
student1.name = 'da zhuang'
student1.gender = 'male'
# define variables for L_obj2
student2.name = 'xiao hong'
student2.gender = 'female'
student2.age = 20

print student1.__dict__.keys()
print student2.__dict__.keys()
['gender', 'name']
['gender', 'age', 'name']

在上面的例子中,我们分别为student1student2两个实例定义了变量,其中student1有两个变量namegender,而student2还多了age变量。上面代码中的__dict__是Python类的内置属性,它是一个存储了类的所有方法或对象的所有属性键值对的字典。

虽然上面的例子显示了Python定义实例变量的灵活性,但是这种方式没有充分利用到面向对象编程的优势。实际上,我们可以在定义类的时候给定变量,从而使得所有的实例都同时具有这些变量,我们将在下面介绍。

类的变量和方法

__init__方法

__init__方法是类的一种特殊方法,被陈为类的构造函数或初始化方法,当创建这个类的实例时就会自动调用该方法。下面我们重新定义一下Student类,以演示__init__方法的使用:

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age

student1 = Student('da zhuang', 'male', 21)
student2 = Student('xiao hong', 'famale', 20)

print student1.__dict__.keys()
print student2.__dict__.keys()
['gender', 'age', 'name']
['gender', 'age', 'name']

在上面的代码中,我们利用__init__方法定义了类的三个变量namegenderage,这些变量被自动应用于类的所有实例。self代表类的实例,是在定义类方法时必须的,虽然在调用时不必为self传入任何参数。

当然,与普通的函数类似,我们也可以为这些变量指定初始值,例如:

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age=23):
        self.name = name
        self.gender = gender
        self.age = age        

student1 = Student('da zhuang', 'male')
print student1.__dict__
{'gender': 'male', 'age': 23, 'name': 'da zhuang'}

自定义方法

类的方法与普通函数类似,只有一个特别之处,即需要一个额外的第一个参数,就是我们上面讲的self参数(按惯例使用这个名称,但不是必须的,可以随意指定其他名称)。例如,我们将上述Student类加入一个自定义的方法:

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age=23):
        self.name = name
        self.gender = gender
        self.age = age
    
    def report(self):
        print "%s is a %d years old %s student." % (self.name.title(), self.age, self.gender)

student1 = Student('da zhuang', 'male', 21)
student2 = Student('xiao hong', 'female', 20)

student1.report()
student2.report()
Da Zhuang is a 21 years old male student.
Xiao Hong is a 20 years old female student.

类的属性

上面类中定义的类变量即为类的实例的属性,可以通过点号.访问,我们上面的例子中已经用到了。这里介绍一下类的内置属性,一共有一下几个:

例如:

print "Student.__doc__:", Student.__doc__
print "Student.__name__:", Student.__name__
print "Student.__module__:", Student.__module__
print "Student.__bases__:", Student.__bases__
print "Student.__dict__:", Student.__dict__
Student.__doc__: 所有学生的基类
Student.__name__: Student
Student.__module__: __main__
Student.__bases__: ()
Student.__dict__: {'report': <function report at 0x7f1e70f29aa0>, '__module__': '__main__', '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\xad\xa6\xe7\x94\x9f\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x7f1e70f29938>}

注意对类调用__dict__属性和对类的实例调用__dict__效果是不一样的。对类调用时会列出类的方法和属性,但是对实例调用时,只会列出实例的属性,而不会列出方法。

print Student.__dict__
print student1.__dict__
{'report': <function report at 0x7f1e70f29aa0>, '__module__': '__main__', '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\xad\xa6\xe7\x94\x9f\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x7f1e70f29938>}
{'gender': 'male', 'age': 21, 'name': 'da zhuang'}

私有属性和方法

类的私有属性

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

类的私有方法

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

单下划线、双下划线、头尾双下划线说明:

类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。通过继承创建的新类称为子类派生类,被继承的类称为基类父类超类。通过继承,子类自动继承父类的所有方法和属性。创建子类的语法如下:

class DerivedClassName(BaseCalssName):
    <statement-1>
    ...
    ...
    <statement-N>

单一继承

只从一个父类来继承,例如:

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age=23):
        self.name = name
        self.gender = gender
        self.age = age
    
    def report(self):
        print "%s is a %d years old %s student." % (self.name.title(), self.age, self.gender)
        
class Boy(Student):
    '''Boy类继承自Student类'''
    def play(self):
        print '%s is playing soccer.' % self.name.title()
boy1 = Boy('da zhuang', 'male', 12)
boy1.report()
boy1.play()
Da Zhuang is a 12 years old male student.
Da Zhuang is playing soccer.

多重继承

也可以从多个父类继承,例如:

class A:
    def a_print(self):
        print 'This is class A.'

class B:
    def b_print(self):
        print 'This is class B.'
        
class C(A, B):
    def c_print(self):
        print 'This is class C.'


c = C()
c.a_print()
c.b_print()
c.c_print()
This is class A.
This is class B.
This is class C.

方法重写

如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:

class Parent:        # 定义父类
    def myMethod(self):
        print '调用父类方法'
    
class Child(Parent): # 定义子类
    def myMethod(self):
        print '调用子类方法'
    
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法
调用子类方法

运算符重载(operator overloading)

运算符重载有时被称为魔术方法(magic methods),指的是利用一些特殊方法来改变Python运算符的行为。特殊方法由连续的两组双下划线(__)包围,有的人也将其称为dunder方法。例如,运算符加号(+),对不同的操作变量有不同的行为:

4 + 6, 'M' + 'R'
(10, 'MR')

从输出结果我们看到了加号运算的不同行为:对于数字,加号代表两数相加;而对于字符串,加号代表连接两个字符串。根据操作对象的不同,加号运算有着不同的行为,实际上,在后台运行的就是特殊方法。对两个整数相加,+调用的是int.__add__()方法;而对于字符串,则调用str.__add__()方法。例如上面代码也可以写成:

int.__add__(4, 6), str.__add__('M', 'R')
(10, 'MR')

因此,我们可以用__add__()方法自定义加法运算。下面我们将前面定义的Student类的两个实例进行相加,看看会出现什么结果?

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age=23):
        self.name = name
        self.gender = gender
        self.age = age
    
    def report(self):
        print "%s is a %d years old %s student." % (self.name.title(), self.age, self.gender)

student1 = Student('da zhuang', 'male', 21)
student2 = Student('xiao hong', 'female', 20)

student1 + student2
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-44-c9dbee72f74f> in <module>()
     12 student2 = Student('xiao hong', 'female', 20)
     13 
---> 14 student1 + student2


TypeError: unsupported operand type(s) for +: 'instance' and 'instance'

我们看到错误提示unsupported operand type(s) for+: 'instance' and 'instance',即加号运算符不支持两个实例来进行运算。这时,我们就可以在类中对加号运算符进行重载,以使其支持两个实例的相加。比如,我们可以定义两个实例相加的运算为两个实例中age属性的相加。

class Student:
    '''所有学生的基类'''
    def __init__(self, name, gender, age=23):
        self.name = name
        self.gender = gender
        self.age = age
    
    def __add__(self, other):
        '''重载加法运算'''
        return self.age + other.age
    
    def report(self):
        print "%s is a %d years old %s student." % (self.name.title(), self.age, self.gender)

student1 = Student('da zhuang', 'male', 21)
student2 = Student('xiao hong', 'female', 20)

student1 + student2
41

从输出可见,加号实现了对两个实例的相加。

以上是关于类的一些基本内容,当然类的应用还有很多知识需要学习,这是面向对象编程的核心,如果大家在以后的使用中遇到问题,可以从网上寻找对应的解决方法。


返回目录

上一篇下一篇

猜你喜欢

热点阅读