面向对象编程

python是一门面向对象编程语言,对面向对象语言编程的过程叫做面向对象.

面向对象程序设计把计算机程序视为一组对象的集合,每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序执行的就是一系列消息在各个对象之间传递.

在python中,所有数据类型都被视为对象,也可以自定义对象.自定义对象数据类型就是面对对象中的类(Class)的概念.

面向对象术语简介:

类: 用来描述具有相同属性和方法的对象集合.类定义了集合中每个对象共有的属性和方法.对象式类的实例.

类变量(属性): 类变量在整个实例化的对象中是公用的.类变量定义在类中,且在方法之外.类变量通常不作为实例变量使用.类变量也称作属性.

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

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

实例变量: 定义在方法中的变量只能作用于当前实例的类.

多态(Polymorphism): 对不同类的对象使用同样的操作.

封装(Encapsulation): 对外部世界隐藏对象的工作细节.

继承(Inheritance): 即一个派生类(derived class)继承基类(base class)的字段和方法.继承允许把一个派生类的对象作为一个基类对象对待,以普通类为基础建立专门的类对象.

实例化(Instance): 创建一个类的实例,类的具体对象.

方法:类中定义的函数.

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

python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类、派生类可以覆盖基类中的任何方法、方法中可以调用基类中同名方法.

对象可以包含任意数量和类型的数据.

类的定义与使用

类的定义

类的语法格式如下:

      class ClassName(object):

   <statement-1>

.

.

.

       <statement-N>

由代码段和类的定义我们看到,python中定义类使用class关键字,class后面紧接着类名,类名通常是大写开头的字母;紧接着是(object),表示该类从哪个类继承下来的.通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类.类包含属性和方法(相当于函数中的语句和方法).

注意:在类中定义方法的形式和函数差不多,但是不称为函数,而称为方法.方法的调用需要绑定到特定对象上,而函数不需要.

类的使用

例如:

class MyClass(object):    i = 123    def f(self):          return 'helloworkd'user_class = MyClass()print('调用类的属性:',user_class.i)print('调用类的方法:',user_class.f())

C:\python\python.exe C:/python.py/lei.py

调用类的属性: 123

调用类的方法: helloworkd

由上例的调用方式可知,类的用比函数多了几个操作,调用类时需要执行如下操作:

user_class = MyClass()

这步叫做实例化,即创建一个类的实例.此处得到的use_class变量称为类的具体对象.

user_class.i用于调用类的属性,也就是所谓的类变量.

user_class.f()用于调用类的方法.

对于类中定义方法的要求:在类中定义方法时,第一个参数必须式self.除第一个参数外,类的方法和普通函数没什么区别,如可以使用默认参数、可变参数、和命名关键字参数等.

对于在类中调用方法的要求:要调用一个方法,在实例变量上直接调用即可.除了self不用传递,其他参数正常传入.

类对象支持两种操作,即属性引用和实例化.属性引用的标准语法如下:

obj.name

语法中obj.代表类对象,name代表属性.

深入类

类的构造方法

例如:

class MyClass(object):    i = 123    def __init__(self,name):        self.name = name    def f(self):         return  'hello,'+self.nameuser_class = MyClass('duyuheng')print('调用类的属性:',user_class.i)print('调用类的方法:',user_class.f())

C:\python\python.exe C:/python.py/lei.py

调用类的属性: 123

调用类的方法: hello,duyuheng

从上例输出结果可以看到,实例化MyClass类时调用了__init__()方法.

在python中,__init__()方法是一个特殊方法,在对象爱实例化时会被调用__init__()的意思式初始化,式initialization的简写.这个方法的书写方式是:先输入两个下划线,后接着init在接着两个下划线,这个方法也叫构造方法.

在定义类时,若不显示到定义一个__init__()方法,则程序默认调用一个无参的__init__()方法.

代码对比:

代码一:使用__init__()方法

class DefaultInit(object):    def __init__(self):        print('类实例化时执行我,我是__init__方法.')    def show(self):        print('我是类中定义的方法,需要通过实例化对象调用.')test =DefaultInit()print('类实例化结束.')test.show()

C:\python\python.exe C:/python.py/lei.py

类实例化时执行我,我是__init__方法.

类实例化结束.

我是类中定义的方法,需要通过实例化对象调用.

代码二:不使用__init__方法

class DefaultInit(object):    def show(self):        print('我是类中定义的方法,需要通过实例化对象调用.')test =DefaultInit()print('类实例化结束.')test.show()

C:\python\python.exe C:/python.py/lei.py

类实例化结束.

我是类中定义的方法,需要通过实例化对象调用.

从上面两段代码可以看出,当代码中定义了__init__()方法时,实例化类时会调用该方法;若没有定义__init__()方法,也不会报错,此时调用默认的__init__()方法.

在python中定义类时若没有定义构造方法(__init__()方法),则在类的实例化时系统调用默认的构造方法.另外,__init__()方法可以有参数,参数通过_=__init__()传递到类的实例化操作上.

__init__()方法是python中构造方法,那么是否可以在一个类中定义多个构造方法呢?

例一:只有一个不带参数的__init__()方法

class DefaultInit(object):    def __init__(self):        print('我是不带参数的__init__()方法.')DefaultInit()print('类实例化结束.')

C:\python\python.exe C:/python.py/lei.py

我是不带参数的__init__()方法.

类实例化结束.

只有一个__init__()方法时,实例化类时没有什么顾虑.

例二:第一个为不带参数的,第二个为带参数的__init__()方法

class DefaultInit(object):    def __init__(self):        print('我是不带参数的__init__()方法.')    def __init__(self,param):        print('我是带参数的__init__()方法,参数值为:',param)DefaultInit('hello')print('类实例化结束.')

C:\python\python.exe C:/python.py/lei.py

我是带参数的__init__()方法,参数值为: hello

类实例化结束.

由执行结果可以看到,调用的是一个带pram参数的构造方法.

注意:此例实例化类时只能调用带两个占位参数的构造方法,调用其他的方法都会报错.

例三:第一个为带参数的,第二个为不带参数的__init__()方法

class DefaultInit(object):    def __init__(self, param):        print('我是带参数的__init__()方法,参数值为:', param)    def __init__(self):        print('我是不带参数的__init__()方法.')DefaultInit()print('类实例化结束.')

C:\python\python.exe C:/python.py/lei.py

我是不带参数的__init__()方法.

类实例化结束.

由执行结果可以看到,调用的构造方法除了self外,没有其他参数.

注意:此例实例化类时只能调用一个带占位参数的构造方法,调用其他构造方法都会报错.

结论:一个类中可以定义多个构造方法,但是实例化类时只能实例化到最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化.建议一个类中只定义一个构造函数.\

类的访问权限

在类内部有属性和方法,外部代码可以通过直接调用实例变量的方法操作数据,这样就隐藏了内部的复杂逻辑.

例如:

class Student(object):    def __init__(self,name,score):        self.name = name        self.score = score    def info(self):        print('学生:%s; 分数:%s '%(self.name,self.score))stu = Student('duyuheng',95)print('修改前分数:',stu.score)stu.info()stu.score=0print('修改后分数:',stu.score)stu.info()

C:\python\python.exe C:/python.py/lei.py

修改前分数: 95

学生:duyuheng; 分数:95 

修改后分数: 0

学生:duyuheng; 分数:0 

由输出结果可以看出,在类中定义非构造方法可以调用类中的构造方法实现变量的属性,调用的方式为self.实例变量属性名,如上例中的self.name和self.score.可以在类的外部修改类的内部数属性.

要是让内部属性不被外部访问,可以在属性名前面加两个下划线__.在python中,实例的变量名如果以__开头,就会变成私有变量(private),只有内部可以访问,外部不能访问.

例如:

class Student(object):    def __init__(self,name,score):        self.__name = name        self.__score = score    def info(self):        print('学生:%s; 分数:%s '%(self.__name,self.__score))stu = Student('duyuheng',95)print('修改前分数:',stu.__score)stu.info()stu.score=0print('修改后分数:',__stu.score)stu.info()

C:\python\python.exe C:/python.py/lei.py

Traceback (most recent call last):

  File "C:/python.py/lei.py", line 14, in <module>

    print('修改前分数:',stu.__score)

AttributeError: 'Student' object has no attribute '__score'

从输出结果可以看出,已经无法从外部访问实例变量的属性_score了,作用就是确保外部代码不能随意修改对象内部的状态,通过访问限制的保护,代码更加安全.

在python中,可以为类增加get_attrs方法,获取类中的私有变量,

例如:

class Student(object):    def __init__(self,name,score):        self.__name = name        self.__score = score    def info(self):        print('学生:%s; 分数:%s '%(self.__name,self.__score))    def get_score(self):        return  self.__scorestu = Student('duyuheng',95)print('修改前分数:',stu.get_score())stu.info()print('修改后分数:',stu.get_score())stu.info()

C:\python\python.exe C:/python.py/lei.py

修改前分数: 95

学生:duyuheng; 分数:95 

修改后分数: 95

学生:duyuheng; 分数:95 

由执行结果可以看到,通过get_score方法已经可以正确得到类内部的属性值.

在python中,可可以为类增加set_attrs方法,修改类中的私有变量.

例如:

class Student(object):    def __init__(self,name,score):        self.__name = name        self.__score = score    def info(self):        print('学生:%s; 分数:%s '%(self.__name,self.__score))    def get_score(self):        return  self.__score    def set_score(self,score):        self.__score=scorestu = Student('duyuheng',95)print('修改前分数:',stu.get_score())stu.info()stu.set_score(0)print('修改后分数:',stu.get_score())stu.info()

C:\python\python.exe C:/python.py/lei.py

修改前分数: 95

学生:duyuheng; 分数:95 

修改后分数: 0

学生:duyuheng; 分数:0 

由执行结果看到,通过set_score方法正确更改了私有变量score的值.

在python中,通过自定义私有变量可对应的set方法可以做参数检查,避免传入无效的参数.

例如:

class Student(object):    def __init__(self,name,score):        self.__name=name        self.__score=score    def info(self):        print('学生:%s; 分数:%s '%(self.__name,self.__score))    def  get_score(self):        return self.__score    def set_score(self,score):        if 0<=score<=100:            self.__score=score        else:             print('请输入0到100的数字.')stu =Student('duyuheng',95)print('修改前的分数',stu.get_score())stu.info()stu.set_score(-10)print('修改后的分数',stu.get_score())stu.info()

C:\python\python.exe C:/python.py/lei.py

修改前的分数 95

学生:duyuheng; 分数:95 

请输入0到100的数字.

修改后的分数 95

学生:duyuheng; 分数:95 

上例输出结果可以看到,调用set_score方法时,如果传入的参数不满足条件,就按照不满足条件的程序逻辑执行.

类的私有方法也是由两个下划线开头,声明该方法为私有方法,且不能在类外使用.

私有方法的调用方式如下:

self.__private_methods

私有方法的使用:

例如:

class PrivatePublicMethod(object):    def __init__(self):        pass    def __foo(self):        print('这是私有方法')    def foo(self):        print('这是公共方法')        print('公共方法中调用私有方法')        self.__foo()        print('公共方法调用私有方法结束')pri_pub = PrivatePublicMethod()print('开始调用公共方法:')pri_pub.foo()print('开始调用私有方法:')pri_pub.__foo()

C:\python\python.exe C:/python.py/lei.py

Traceback (most recent call last):

  File "C:/python.py/lei.py", line 21, in <module>

    pri_pub.__foo()

AttributeError: 'PrivatePublicMethod' object has no attribute '__foo'

开始调用公共方法:

这是公共方法

公共方法中调用私有方法

这是私有方法

公共方法调用私有方法结束

开始调用私有方法:

由输出结果可以看出,私有方法和私有变量类似,不能通过外部调用.

继承

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

在面向对象程序设计中,当我们定义一个class时,可以从某个现有的class继承,定义的新class称为子类(Subclass),而被继承的class称为基类,父类或超类(Base class、Super class).

继承定义如下:

clss DerivedClassName(BaseClassName):

<statement-1>

.

.

<statement-N>

需要注意:继承语法class子类名(基类名)时,//基类名写在括号里,基本类是在定义类时,在元组中指明的.

在python中,继承有以下特点:

(1)在继承中,类级泵的构造方法(__init__()方法)不会被自动调用,需要在子类的构造方法中专门调用.

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

(3)在python中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找

例如:

class Animal(object):    def run(self):        print('Animal is runing...')#上面定义了一个名为Animal的类,类中定义了一个run()方法直接输出(默认调用__init__()方法)class Dog(Animal):        passclass Cat(Animal):        pass#上面代码对于Dog和Cat来说Animal就是它的父类;对于Animal来说Dog和Cat来说Animal就是它的子类.dog = Dog()dog.run()cat = Cat()cat.run()

C:\python\python.exe C:/python.py/lei.py

Animal is runing...

Animal is runing...

由输出结果看到,子类中没有定义任何方法,但是都执行了run()方法.

继承的优点;

继承最大的好处时子类获得了父类全部非私有的功能.

子类也可以拥有自己的方法.

例如:

class Animal(object):    def run(self):        print('Animal is runing...')class Dog(Animal):        def eat(self):            print('Eating...')dog = Dog()dog.run()dog.eat()

C:\python\python.exe C:/python.py/lei.py

Animal is runing...

Eating...

由上例可以看出,唉Dog中增加了eat方法,从输出结果可以看书既执行了父类的方法,又执行了自己定义的方法.

若不想让子类继承父类中的私有方法,也不能调用父类的私有方法.父类定义如下.

例如:

class Animal(object):    def run(self):        print('Animal is runing...')    def __run(self):        print('I am private method.')class Dog(Animal):        passdog=Dog()dog.__run()

C:\python\python.exe C:/python.py/lei.py

Traceback (most recent call last):

  File "C:/python.py/lei.py", line 15, in <module>

    dog.__run()

AttributeError: 'Dog' object has no attribute '__run'

由执行结果可以看到,子类不能调用父类的私有方法,子类虽然继承了父类,但是调用父类的私有方法相当于从外部调用类中的方法,因而调用不成功.

对于类中扩展的非私有方法,子类可以拿来即用,如在父类Animal中增加一个jump方法:

class Animal(object):    def run(self):        print('Animal is runing...')    def jump(self):        print('Animal is jumpping...')    def __run(self):        print('I am a private method.')class Dog(Animal):        passclass Cat(Animal):    passdog = Dog()dog.run()dog.jump()cat =Cat()cat.run()cat.jump()

C:\python\python.exe C:/python.py/lei.py

Animal is runing...

Animal is jumpping...

Animal is runing...

Animal is jumpping...

由上例结果可以看出,子例可以立即获取父类增加的非私有方法.

多态

多台来自于希腊语,意思时有多种形式.多态意味着即使不知道变量所引用的对象类型是什么,也能对对象进行操作,多态会根据对象(或类)的不同而表现出不同的行为.

例如:

class Animal(object):    def run(self):        print('Animal is runing...')class Dog(Animal):    def run(self):        print('Dog is running...')class Cat(Animal):    def run(self):        print('Cat in runing...')dog = Dog()print('实例化Dog类')dog.run()cat = Cat()print('实例化Cat类')cat.run()

C:\python\python.exe C:/python.py/lei.py

实例化Dog类

Dog is running...

实例化Cat类

Cat in runing...

在上例中我们在Animal类中定义了run方法,Dog和Cat类分别继承Animal类,并且分别定义了自己的run方法,最后Dog和Cat调用的是自己定义的run方法.

当我们定义类时实际上就定义了一中数据类型.定意义数据类型和python自带的数据类型(如str、list、dict)没什么两样.

例如:

class Animal(object):    def run(self):        print('Animal is runing...')class Dog(Animal):    def run(self):        print('Dog is running...')class Cat(Animal):    def run(self):        print('Cat in runing...')a=list()b=Animal()c=Dog()print('a是否为list类型:',isinstance(a,list))print('b是否为Animal类型:',isinstance(b,Animal))print('c是否为Animal类型:',isinstance(c,Dog))

C:\python\python.exe C:/python.py/lei.py

a是否为list类型: True

b是否为Animal类型: True

c是否为Animal类型: True

输出结果可以看出:a,b,c确实分别为list,Animal,Dog三种类型

在执行下列语句:

print('c是否为Dog类型:',isinstance(c,Dog))print('c是否为Animal类型:',isinstance(c,Dog))

C:\python\python.exe C:/python.py/lei.py

c是否为Dog类型: True

c是否为Animal类型: True

由执行结果可以看出,c既是Dog类型又是Animal类型,应为Dog是从Animal继承下来的,当我们创建Dog实例时,我们认为c的数据类型是Dog,但是c同时也是Animal,Dog本来就是Animal的一种.

在继承关系中,如果一个实例的数据类型是某个子类,那么他的数据类型也可以看作时父类.但是反过来就不行了:

在执行下列语句:

print('b是否为Dog类型:',isinstance(b,Dog))

C:\python\python.exe C:/python.py/lei.py

b是否为Dog类型: False

我们在看一个实例.编写一个函数,这个函数接受一个Animal类型的变量,定义并执行如下函数,执行时传入Animal的实例:

class Animal(object):    def run(self):        print('Animal is runing...')class Dog(Animal):    def run(self):        print('Dog is running...')class Cat(Animal):    def run(self):        print('Cat in runing...')def run_two_times(animal):    animal.run()    animal.run()run_two_times(Animal())

C:\python\python.exe C:/python.py/lei.py

Animal is runing...

Animal is runing...

若执行函数时传入Dog的实例,如下:

run_two_times(Dog())

C:\python\python.exe C:/python.py/lei.py

Dog is running...

Dog is running...

若执行函数时传入Cat的实例,如下:

run_two_times(Cat())

C:\python\python.exe C:/python.py/lei.py

Cat in runing...

Cat in runing...

如果在定义一个Bird类型,也继承Animal类,定义如下:

class Bird(Animal):    def run(self):        print('Bird is flying the sky ...')run_two_times(Bird())

C:\python\python.exe C:/python.py/lei.py

Bird is flying the sky ...

Bird is flying the sky 

由执行结果可以看出,新增的Animal子类不必对run_two_times()方法做任何修改.实际上,任何依赖Animal作为参数的函数或方法都可以不加修改地正常运行,原因就在于多态.

多态的好处是:我们需要传入子类时,只需要接受父类型就可以了,因为子类都是父类型,按照父类型操作即可.由于父类型有run()方法,因此传入类型只要是父类或者是继承父类,都会自动调用实际类型的run()方法.

多态的意思:对于一个变量,我们只需要知道它是父类型,无需确切的知道它的子类型,就可以放心调用run()方法,.具体调用的run()方法作用于父类或者子类,由运行时该对象的确切类型决定.

多态真正的威力在于:调用方只管调用,不管细节.当我们新增一种Animal的子类时,只要确保run()方法编写正确即可,不用管原来的代码是如何调用的.这就是"开闭"原则:对于扩展开放,允许新增Animal子类;对于封闭,不需要修改依赖Animal类型的run_two_times()等函数.

很多函数和运算符都是多态的,只要使用多态函数和运算符,多态就会消除.唯一能毁掉多态的是使用函数显示地检查类型,如type、isinstance函数等.如果有可能,就尽量避免使用这些毁掉多态的方式,重要的是如何让对象按照我们希望的方式工作,无论它是否是正确类型或类.

封装

封装是全局作用域中其他区域隐藏多余信息的原则.听起来像多态,使用对象而不用知道其他内部的细节.

封装并不等同于多态.多态可以让用户不知道类(或对象类型)的对象进行方法调用,而封装可以不用关心对象是如何构建的,直接使用即可.

例如:

class Student(object):    def __init__(self,name,score):        self.name = name        self.score = scorestd = Student('duyuheng',90)def info(std):    print('学生:%s; 分数:%s;'%(std.name,std.score))info(std)

C:\python\python.exe C:/python.py/lei.py

学生:duyuheng; 分数:90;

上例是通过函数调用并得到结果.

可以直接在Studnet类内部定义访问数据的函数,这样就把"数据"封装起来了.这些封装数据的函数数据和Student类本身是相关联的,我称之为方法.

要定义一个方法,除了第一参数时self外,其他参数和普通函数一样.要调用一个方法,在实例变量上直接调用即可.除了self不用传递,其他参数正常传入.

class Student0(object):    def __init__(self,name,score):        self.name = name        self.score = scorestu = Student0('xuwei',99)def info(self):    print('学生:%s  分数:%s'%(self.name,self.score))info(stu)

C:\python\python.exe C:/python.py/lei.py

学生:xuwei  分数:99

由上例我们从外部看出Studnt类,只需要知道创建实例需要给出的name和score,如何输出是在Student类的内部定义的,这些数据和逻辑被"封装"起来,调用很容易,但却不知道内部实现的细节.

封装的另一个好处时可以给student类增加新方法,比如访问权限中的get_score()方法和set_score()方法,使用这些方法时,无需知道内部的实现细节,直接调用即可.

多重继承

多重继承的定义如下:

class DerivedclassName(Base1,Base2,Base3)

<statement-1>

.

.

<statement-N>

多重继承就是使有多个基类(父类或超类).

需要注意圆括号中父类的顺序,若父类中有相同的方法名,在子类使用时未指定,python会从左到右搜索.若方法在子类中未找到,则从左到右查找父类中是否包含方法.

例如:

class  A(object):    def __init__(self,a):        print('init A...')        self.a = aclass B(A):    def __init__(self,a):        super(B,self).__init__(a)        print('init B...')class C(A):    def __init__(self,a):        super(C,self).__init__(a)        print('init C...')class D(B,C):    def __init__(self,a):        super(D,self).__init__(a)        print('init D...')d =D('d')

C:\python\python.exe C:/python.py/lei.py

init A...

init C...

init B...

init D...

由上列可以看出,D同时继承自B和C,也就是D拥有了A、B、C的全部功能.多重继承通过super()调用__init__()方法时,A虽然被继承了两次但是__init__()只调用一次.

多重继承的目的时从两种继承树中分别选择并继承出子类,以便组合功能使用.

多重继承一个子类可以继承多个父类,同时获得多个父类所有非私有功能

获取对象信息

python提供了3种获取对象类型的方法.

第1种使用type()函数

将一个变量实现哦昂函数或类

print(type(abs))#函数

C:\python\python.exe C:/python.py/lei.py

<class 'builtin_function_or_method'>

class  A(object):#类    def __init__(self,a):      passprint(type( A(object)))

C:\python\python.exe C:/python.py/lei.py

<class '__main__.A'>

由上面两例子可以看出,返回值对应的Class类型.

如果要在if语句中判断并比较两个变量的type类型是否同,如下:

print(type(123)==type(456))print(type(123)==int)print(type('abc')==type('123'))print(type('abc')==str)print(type('abc')==type(123))

C:\python\python.exe C:/python.py/lei.py

True

True

True

True

False

由上例可以看出,判断基本数据类型可以直接写int、str.

可以使用tyoes模块中定义的常量.

例如:

import  typesdef func():    passprint(type(abs)==types.BuiltinFunctionType)print(type(lambda x:x)==types.LambdaType)print(type((x for x in range(10)))==types.GeneratorType)

C:\python\python.exe C:/python.py/lei.py

True

True

True

由上例可以看出,函数的判断方式需要借助types模块的帮助.

第二种使用isinstance()函数

要明确class的继承关系,使用type()很不方便,通过判断class的数据类型确定class的继承关系要方便的多,这个时候要使用isinstance()函数.

例如:

class Animal(object):    def run(self):        passclass Dog(Animal):    def run(self):        passanimal = Animal()dog =Dog()print(isinstance(dog,Dog))print(isinstance(dog,Animal))C:\python\python.exe C:/python.py/lei.pyTrueTrue

由上例可以得知:尽管dog式Dog类型,不过由于Dog是从Animal继承下来的,因此dog也是Animal类型.isinstance()判断的是一个对象是否为该类型的本身,或者是否为该类型继承的类型.

不过animal不是Dog类型

print(isinstance(animal,Dog))

C:\python\python.exe C:/python.py/lei.py

False

需要知道的是:能用type()判断的基本类型也可以用isinstance()判断.

isinstance()可以判断一个变量是否为某些类型中的一种,判断变量是否为list或tuple的方式.print(isinstance([1,2,3],(list,tuple)))print(isinstance((1,2,3),(list,tuple)))

C:\python\python.exe C:/python.py/lei.py

True

True

第三种使用dir()

如果要获得一个对象的所有属性和方法,就可以使用dir()函数.dir()函数返回一个字符串的list.

例如:

print(dir('abc'))

C:\python\python.exe C:/python.py/lei.py

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

类的专有方法

python类可以定义专有方法.专门方法是在特殊情况下或使用特别语法时由python调用的,而不像普通方法一样在代码中直接调用.

看到型如__xxx__的变量或函数名就要注意,这在python中是有特殊用途的.

这种特殊类型的函数定制类的方法由有以下几种.

第一种__str__ 用法如下:

class Studnet(object):    def __init__(self,name):        self.name= name    def __str__(self):        return '学生名称:%s'%self.nameprint(Studnet('duyuheng'))

C:\python\python.exe C:/python.py/lei.py

学生名称:duyuheng

这样的输出不但好看而且,还是我们想要的

在看一下直接显示变量的内容:

class Studnet(object):    def __init__(self,name):        self.name= names=Studnet('duyuheng')print(s)

C:\python\python.exe C:/python.py/lei.py

<__main__.Studnet object at 0x00000000010CB4E0>

上例输出结果为一堆字符串,这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别在于__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符.也就是说,__repr__()是说,__repr__()是为调试服务的.

解决这个办法是在定义一个__repr__().通常,__str__()和__repr__()代码是一样的,所以有一个简单的写法如下:

class Student(object):    def __init__(self,name):        self.name = name    def __str__(self):        return '学生名称:%s' % self.name    __repr__=__str__s = Student('duyuheng')print(s)

C:\python\python.exe C:/python.py/lei.py

学生名称:duyuheng

上例已经得到满意的结果

第二种__iter__

如果想将一个类用于for...in循环,类似list或tuple一样,就必须实现一个__iter__()方法.该方法返回一个迭代对象,python的for循环会不断调用该迭代对象的__next__()方法,获得循环的下一个值,直到遇到StopIteration错误时退出循环.

以裴波那契(黄金分数列)为例,写一个可以作用于for循环的Fib类:

class Fib(object):    def __init__(self):        #初始化两个计数器a、b        self.a,self.b = 0,1    def __iter__(self):        #实例本身就是就是迭代对象,故返回自己        return self    def __next__(self):        #计算下一个值        self.a,self.b=self.b,self.a+self.b        #退出循环条件        if self.a > 100:            raise StopAsyncIteration();        #返回下一个值        return self.afor n in Fib():    print(n)

C:\python\python.exe C:/python.py/lei.py

1

1

2

3

5

8

13

21

34

55

89

第三种__getitem__

要像list一样按照下标取出元素,需要实现__getitem__()方法.

例如:

class Fib(object):    def __getitem__(self, n):        a,b =1,1        for x in range(n):            a,b =b,a+b        return  afib = Fib()print(fib[3])print(fib[10])

C:\python\python.exe C:/python.py/lei.py

3

89

由执行结果看到,可以成功获取对应数列的值了.

第四种__getattr__

正常情况下,调用类的方法或属性时,如果类的方法或属性不存在就会报错,python提供了一种机制,就是写一个__getattr__()方法,动态返回一个属性.

class Student(object):    def __init__(self):        self.name ='duyuheng'    def __getattr__(self, attr):        if attr=='score':            return  95stu = Student()print(stu.name)print(stu.score)

C:\python\python.exe C:/python.py/lei.py

duyuheng

95

注意:只有在没有找到属性的情况下才会调用__getattr__,已有的属性(如name),不会在__getatter__中查找,此外,如果所有调用都会返回None(如stu.abc),就是定义的__getattr__默认返回None.

第五种__call__

一个对象实例可以有自己的属性和方法,调用实例的方法时使用instance.method()调用.

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用.

例如:

class  Student(object):    def __init__(self,name):        self.name = name    def __call__(self):        print('名称:%s'%self.name)stu=Student('duyuheng')stu()

C:\python\python.exe C:/python.py/lei.py

名称:duyuheng

由上例输出结果可以看到,直接对实例进行调用并得到结果.

__call__()还可以定义参数.对实例进行直接调用就像对一个函数调用一样,完全可以把对象看成函数,把函数看成对象,因为这两者本身就有根本区别.

如果把对象看成函数,函数本身就可以在运行期间动态创建出来,因为类的实例都是运行期间创建出来的,就模糊了对象和函数的界限.

很时候判断一个对象是否能被调用,可以使用Callable()函数,比如函数上例定义的带有__call__()的类实例.

print(callable((Student('duyuheng'))))print(callable(max))print(callable([1,2,3]))print(callable(None))print(callable('a'))

C:\python\python.exe C:/python.py/lei.py

名称:duyuheng

True

True

False

False

False

由操作结果可以看到,通过callable()函数可以判断一个对象是否为"可调用"对象