python大道至简(第六部分)

作者:下家山(qq:1209050967,微信:xiajiashan)

python高级

一:python类

Python从设计之初,就立足于面向对象,它的一个变量,实际上就是一个类对象。我们来看一个实验:

这里,变量i并非像面向过程语言中的变量,是一个单值量,而是一个类对象(继承了很多东西),因为我们定义的这个变量,赋初值为0.,所以Python把它当成一个整型变量,所以我们打印它的__class__属性(__class__指类名),看到的是’int’,而它的__doc__属性(__doc__属性指帮助信息)为class。

下面是变量i后面按下.之后列出的信息:

如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。

接下来我们先来简单的了解下面向对象的一些基本特征。

1.1:面向对象技术简介

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

·       类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外类变量通常不作为实例变量使用

·       数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。

·       方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖override),也称为方法的重写

·       实例变量:定义在方法中的变量,只作用于当前实例的类。

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

·       实例化:创建一个类的实例,类的具体对象。

·       方法:类中定义的函数

·       对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量实例变量)和方法

·        

1.2 创建类

使用class语句来创建一个新类,class之后为类的名称并以冒号结尾,如下实例:

class ClassName:
   '类的帮助信息'   #类文档字符串
   class_suite  #类体

类的帮助信息可以通过ClassName.__doc__查看。

class_suite 由类成员,方法,数据属性组成,class_suite并不是一个关键字。

1.3实例

以下是一个简单的Python类实例:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

classEmployee:

'所有员工的基类'               #注意这不是注释,是类属性的一部分,撰写类的帮助信息

empCount = 0              #类变量

def__init__(self, name, salary):     #__init__是类的方法——构造函数,namesalary是实例变量,也叫实例化变量

self.name = name                     #给类实例化,就是赋予实际的意义(就是实际的值)

self.salary = salary

Employee.empCount += 1              #在类内部使用类变量,与其他实例是共享的,相当于全局变量

defdisplayCount(self):                  #普通方法(与__init__不一样)

print"Total Employee%d" % Employee.empCount

defdisplayEmployee(self):              #普通方法

print"Name : ", self.name, ", Salary: ", self.salary

·       empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。

·       第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法

如何使用这个类呢?

创建实例对象

实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。

以下使用类的名称 Employee 来实例化,实例化的过程中是通过 __init__ 方法来接受参数的。

"创建 Employee 类的第一个对象"

emp1 =Employee("张三",8000)

"创建 Employee 类的第二个对象"

emp2 =Employee("王五",10000)

访问属性

您可以使用点(.)来访问对象的属性。使用类的名称访问类变量:

emp1.displayEmployee()           #访问类属性,即方法,也可叫类里面的函数,本质一样,只是叫法不一样
emp2.displayEmployee()           #
print "Total Employee %d" % Employee.empCount      #通过类名访问类变量

完整实例:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

classEmployee:

'所有员工的基类'

empCount = 0

def__init__(self, name, salary):

self.name = name

self.salary = salary

Employee.empCount += 1

defdisplayCount(self):

print"Total Employee %d" % Employee.empCount

defdisplayEmployee(self):

print"Name : ", self.name, ", Salary: ", self.salary

 

#创建 Employee 类的第一个对象

emp1 = Employee("张三", 8000)

#创建 Employee 类的第二个对象

emp2 = Employee("王五", 10000)

emp1.displayEmployee()

emp2.displayEmployee()

print"Total Employee%d" % Employee.empCount

执行以上代码输出结果如下:

Name:  张三,Salary:  8000

Name:  王五,Salary:  10000

TotalEmployee2

Pycharm的实验结果

因为觉得displayCount方法意义不大,所以没有放进去。

看看__doc__帮助信息:

__doc__相当于一个类的描述信息,有时候也很有用。

添加属性

我们这里添加了属性age,但是添加之后想打印出来看看,发现打印不出来,为什么呢?

因为,我们在类中的方法DisplayEmployee中并没有age(年龄)的打印代码。所以不可能打印age出来。

那么,怎么知道age属性添加进到对象emp1中去了呢?

我们可以通过dir这个方法来列出对象emp1的所有属性,我们看到了age

改变属性值

我们可以直接通过对象访问其属性,并进行修改。

删除属性

 

因为在第16行已经删除了属性age,所以第17行再要打印这个属性值,将错误!

1.4 python中操作类的内置方法

·       getattr(obj, name[, default]) : 访问对象的属性。

·       hasattr(obj,name) : 检查是否存在一个属性。

·       setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。

·       delattr(obj, name) : 删除属性。

hasattr(emp1, 'age')# 如果存在 'age' 属性返回 True

getattr(emp1, 'age')# 返回 'age' 属性的值

setattr(emp1, 'age', 8)# 添加属性 'age' 值为 8

delattr(empl, 'age')# 删除属性 'age'

以上内容很容易做实验。

 

1.5 关于类中的self

         如果说类有灵魂的话,那么它的灵魂就是self;

这里有个奇怪的问题是:类中定义的方法有self这个形参,但是在实际传参的时候,并没有传参给self,这是为什么呢?

这是因为,self是实例化的一个影子,就是当我们第12条语句执行的时候,就产生了一个对象,这个对象通过name=”张三”,salary=8000实例化了,此时python系统会自动的把这个对象本身赋予self这个参数。此时此刻,似乎很玄妙,第12条语句在创建类的实例emp1的时候,当emp1还没有形成的时候,却有了emp1的一个生命,或者灵魂叫self。而,在第14,15行的时候,我们其实已经有了emp1,但是为什么也不传参给self呢。我想应该是在最开始就可以用self来代表emp1,那接下来的类似应用中都可以self就代表对象本身,而不需要特指谁。

         我想试试,既然使用的时候(第12,13,14,15行代码)都不传参给类中的方法,那么类中的self是否可以不要呢?

         你看,不要self,编译的时候会报错,这个错误信息是说:在执行第12条语句的时候,也就是调用__init__函数的时候,我们传了3个参数进去,而实际上__init__只需要2个参数。

为什么是这样呢?我们不是明明只传两个参数吗,一个名字,一个薪水,怎么说我们传了三个参数呢?

此时,我们应该可以肯定了,python类中的每个方法的调用,系统默认第一个参数就是self了,只是这个self是隐形的,因为它是类对象的灵魂。

我还想做一个实验:既然类中定义的每个方法,第一个参数是self,而且python系统会默认的传一个参数给self,那我可否把对象直接传给它呢?可以吗,应该不行不通,因为我发现,在第12行,对象emp1都还没有生成,你怎么在方法里面把它当参数传进去了?所以,想到这里,就不用做这个实验了。

self代表类的实例,而非类

通过以上实验,我们能够分析出,self特指某一个类的对象的地址。

Self并不是一个关键字

Self并不是一个Python内置的关键字,而是一个约定俗成的一个名字。我们可以取其它名字。

1.6 python对象销毁

1.6.1 自动销毁

注意点:第3行是构造函数,函数名__init__是构造函数的名称,参数x=0,y=0是初始化参数列表,如果在第8行不传参数100200进去,那么P1.x=0P1.y=0

问题是:我__del__函数是什么时候调用的,在哪里调用的?

        其中原理是,当我们运行完,系统会自动的销毁对象P1占用的相关资源。所以,__del__是系统调用的。

1.6.2 通过del命令销毁

1.7 对象的引用

1.7.1 引用和被引用的地址

引用本身不占用内存空间,他只是一跟绳子。

1.7.2 引用计数器

Python 使用了引用计数这一简单技术来跟踪和回收垃圾。

Python 内部记录着所有使用中的对象各有多少引用。

一个内部跟踪变量,称为一个引用计数器。

当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,也就是说,这个对象的引用计数变为0 时,它被垃圾回收。但是回收不是"立即"的,由解释器在适当的时机,将垃圾对象占用的内存空间回收。

 

我想知道,没有对象引用,引用计数器会是多少呢?

默认情况下等于2。为什么?

因为,在sys.getrefcount(P1)中,传入的但是也是一次引用,所以引用计数器=2;

我们再增加一个引用

但是在第12行中,调用sys.getrefcount(P1)并没有继续增加P1的引用计数器,我个人认为,是python系统刻意为之,就是不管sys.getrefcount(P1)调用多少次,只算一次。

1.7.3 删除引用不等于删除对象

第14行删除引用P3(注意,不是删除对象P3,P3不是对象是引用)

引用P3删除后,P1的引用计数器会减一;

这里有个问题:第14行执行,为什么不执行析构函数__del__?

这个原因还是因为第14行删除的不是对象,而是引用造成的。

如果删除P1,再访问P2,P3,P4会出现什么问题呢?这个实验我没有做,有兴趣的可以去做一下。

1.8 继承

1.8.1:何为继承

继承,是自然界普遍存在的一种现象,是从先辈那里得到已有的特征行为。类的继承就是新类从已有类那里获得已有的属性和行为,或者说基类派生了具有基类特征又有新特征的类。

继承是软件可重用性的一种形式,新类通过继承从现有类中吸取属性和行为,并对其进行覆盖和改写,产生新类所需要的功能。

同样的,新类也可以派生出其他更新的类。

1.8.2:继承的语法

子类(派生类)具有父类(基类)的所有属性行为,且可以增加新的行为和属性。

继承语法

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:

class 派生类名(基类名)://... 基类名写在括号里,基本类是在类定义的时候,在元组之中指明的。

classSubClassName(ParentClass1[,ParentClass2,...]):

          'Optional classdocumentation string'   //类的帮助信息

          class_suite        //子类代码部分

比如:class woman(people)//括号里的people是基类名,woman是子类(也叫派生类)。

python中继承中的一些特点:

  1:基类名是一个元组,元素可以是一个,也可以是多个,如ParentClass1ParentClass2

2:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。(下面有实例讲解1.8.3

3:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别在于类中调用普通函数时并不需要带上self参数

4Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作"多重继承"

1.8.3 实例,重写,覆盖

 

现在,我们要通过people这个基类创建一个派生类woman,而且要增加一个属性性别(sex),该怎么做呢?

注意点:

1,  在第9行,添加了子类woman的额外属性sex。

2,  子类的属性一共有4个,其中3个继承自父类,所以在子类的构造函数中,需要传入4个参数。但是在python中,父类的构造函数不会自动调用,需要子类类显示调用,并传入参数self,调用方法见第11行。

3,  因为,woman加入了sex属性,所以woman要介绍自己,就需要重写父类的speak函数,因为父类的speak函数只有名字,年龄,体重三个属性,此时怎么办呢?

那就需要子类重写父类方法speak,加入新的内容。

4,  我们在第16行调用的时候,只会调用子类的speak,父类的speak被覆盖

5,  Super函数的使用见下面的博文。

http://blog.csdn.net/goodluckac/article/details/53100957

1.8.4 子类对象怎么调用父类方法

在python中,子类只能通过super函数调用父类的方法。其实,父类的所有属性和方法都被子类继承了,但是重写的方法只能通过super函数实现。(子类通过super访问父类同名函数,意义不大)

基类的属性因为被全部继承下来了,所以没有必要通过super函数访问,直接通过子类对象即可访问。

1.9 issubclass()isinstance()函数

      Issubclass(cls,classinfo)用于判断cls是不是classinfo的子类;

      Isinstance(o,t)用于判断对象o是不是类t的某一个实例;

      

       当我们在Pycharm中敲入is的时候,pycharm系统会帮我们跳出上面的这个属性联想图,其中最前面的f表示是函数(也即方法),后面的builtins表示是内置函数(方法)。

     这两个函数返回的是布尔变量。

1.10  类的私有和公有(属性/方法)

         在python中,类的私有成员和方法通过双下划线来表示,因此没有双下划线的属性或方法即为公有属性,公有属性可以在类外通过实例化的对象进行访问,但是私有成员和方法不能在类外通过实例化的对象进行访问,只能在类内访问。

     上面是私有成员函数不能在类外访问的情况。

         我们再看看私有成员变量的情况。

私有变量不能在类外直接访问,只能在类内访问(像第10行)。

私有方法,既然不能在类外访问,那么定义出来有何意义?

我们怎么调用__display_all()

发布了38 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_27320195/article/details/79971512
今日推荐