Python 面向对象



Python 面向对象

搞懂Python类、方法、变量、实例化和对象的概念

面向对象技术简介

Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。面向对象编程是在面向过程编程的基础上发展来的,它比面向过程编程具有更强的灵活性和扩展性。面向对象编程是程序员发展的分水岭,很多初学者会因无法理解面向对象而放弃学习编程。本章节我们将详细介绍Python的面向对象编程。
面向对象编程(Object-oriented Programming,简称 OOP),如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。接下来我们先来简单的了解下面向对象的一些基本特征。

类:用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
属性:类中的所有变量称为属性。
方法:类中定义的函数。不过,和函数所有不同的是,类方法至少要包含一个 self 参数。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。
实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
局部变量:定义在方法中的变量,只作用于当前实例的类。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
方法重载:类中支持多个同名函数,但参数的个数或类型要不同,在调用的时候,解释器会根据参数的个数或者类型,调用相应的函数.
封装:将类的某些部分(属性、方法)隐藏起来,具有一定的保护作用,隐藏对象的属性和方法实现细节,仅对外提供公共的访问方式。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
多态:多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承).
实例化:创建一个类的实例,类的具体对象。

定义类

语法格式如下:
 class 类名:
    执行语句
    类变量
    类方法

类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。
类最重要的两部分就是类变量和类方法,类成员之间的可以相互调用。

在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
 class Animal():
    # __init__用于在创建对象时进行初始化操作,通过这个方法可以绑定name和color两个属性
    def __init__( self, name, color):
        self.name = name
        self.color = color

    def play( self, action):
        print('%s正在玩%s.' % (self.name, action))

animal = Animal("dog","black")
animal.play("篮球")

执行结果:
dog正在玩篮球.

写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。当我们定义好一个类之后,可以通过上面的方式来创建对象并调用。
类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样:
 def __init__(self):
    self.data = []

类定义了 __init__() 方法,类的实例化操作会自动调用 __init__() 方法。如下实例化类 Animal,对应的 __init__() 方法就会被调用:
 animal = Animal("dog","black") 

当然, __init__() 方法可以没有参数。
self代表类的实例,而非类,类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的
第一个参数名称, 按照惯例它的名称是 self。
 class Animal:
    def show( self):
        print(self)
        print(self.__class__)
animal = Animal()
animal.show()

执行结果:
<__main__.Animal instance at 0x10ad9d638>
__main__.Animal

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 lidihuo 也是可以正常执行的:
 class Animal():
    def show( lidihuo):
        print(lidihuo)
        print(lidihuo.__class__)
animal = Animal()
animal.show()

执行结果:
<__main__.Animal instance at 0x10ad9d638>
__main__.Animal

访问权限

对于C++、Java、C#等编程语言的属性有4种访问权限,而在在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面看个列子。
 class Animal:
    # __init__用于在创建对象时进行初始化操作,通过这个方法可以绑定name和color两个属性
    def __init__( self, name, color):
        self.name = name
        self.color = color

    def __play( self, action):
        print('%s正在玩%s.' % (self.name, action))

def main():
    animal = Animal("dog","black")
    animal.__play("篮球")
if __name__ == "__main__":
    main()

执行结果:
Traceback (most recent call last):
File "script.py", line 14, in
main()
File "script.py", line 12, in main
animal.__play("篮球")
AttributeError: 'Animal' object has no attribute '__play'

在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问。但让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。

@property装饰器

上面我们讲到python中属性和方法访问权限,我们不建议将属性设置为私有的,但是有时直接将属性暴露给外界也是有问题的,前面我们建议将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。通过@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。
 class Animal:
    def __init__( self, name, color):
        self.name = name
        self.color = color

    # 访问器 - getter方法
    @property
    def name( self):
        return self._name

    # 访问器 - getter方法
    @property
    def color( self):
        return self._color

    # 修改器 - setter方法
    @name.setter
    def name( self, name):
        self._name = name

    # 修改器 - setter方法
    @color.setter
    def color( self, color):
        self._color = color

    def play( self, action):
        print('%s正在玩%s.' % (self.name, action))

def main():
    animal = Animal("dog","black")
    animal.play("篮球")

if __name__ == "__main__":
    main()

执行结果:
dog正在玩篮球.

__slots__魔法

Python是一门动态语言,允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。限定对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。
 class Animal:
    # 限定Person对象只能绑定_name, _color和_age属性
    __slots__ = ('_name', '_color', '_age')
    def __init__( self, name, color):
        self.name = name
        self.color = color

    # 访问器 - getter方法
    @property
    def name( self):
        return self._name

    # 访问器 - getter方法
    @property
    def color( self):
        return self._color

    # 修改器 - setter方法
    @name.setter
    def name( self, name):
        self._name = name

    # 修改器 - setter方法
    @color.setter
    def color( self, color):
        self._color = color

    def play( self, action):
        print('%s正在玩%s.' % (self.name, action))

def main():
    animal = Animal("dog","black")
    animal.play("篮球")
    animal._age = (5)
    animal._weight = (50)

if __name__ == "__main__":
    main()

执行结果:
animal._weight = (50)
AttributeError: 'Animal' object has no attribute '_weight'

静态方法和类方法

@staticmethod,静态方法,通过类直接调用,不需要创建对象,不会隐式传递self
@classmethod,类方法,方法中的self是类本身,调用方法时传的值也必须是类的公有属性,就是说类方法只能操作类本身的公有字段。

 class Animal:
    name = "Dog"
    color = "Black"

    @classmethod
    def play( self, action):
        print('%s正在玩%s.' % (self.name, action))

    @staticmethod
    def show():
        print('%s是%s的.' % (Animal.name,Animal.color))

def main():
    animal = Animal()
    animal.play("篮球")
    Animal.show()
if __name__ == "__main__":
    main()

执行结果:
Dog正在玩篮球.
Dog是Black的.

面向对象的支柱

面向对象有三大支柱:封装、继承和多态。封装就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要调用对象方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数,而不需要知道方法内部的实现细节。继承和多态的内容请点击下面查看