Python3.5修炼手册13

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3.5修炼手册13相关的知识,希望对你有一定的参考价值。

面向对象编程

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.name
user_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=0
print(‘修改后分数:‘,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=0
print(‘修改后分数:‘,__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.__score
stu = 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=score
stu = 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):
        pass
class 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):
        pass
dog=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):
        pass
class Cat(Animal):
    pass
dog = 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 = score
std = 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 = score
stu = 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 = a
class 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):
      pass
print(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  types
def func():
    pass
print(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):
        pass
class Dog(Animal):
    def run(self):
        pass
animal = Animal()
dog =Dog()
print(isinstance(dog,Dog))
print(isinstance(dog,Animal))
C:\python\python.exe C:/python.py/lei.py
True
True

由上例可以得知:尽管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.name
print(Studnet(‘duyuheng‘))

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

学生名称:duyuheng

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

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

class Studnet(object):
    def __init__(self,name):
        self.name= name
s=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.a
for 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  a
fib = 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  95
stu = 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()函数可以判断一个对象是否为"可调用"对象


本文出自 “duyuheng” 博客,谢绝转载!

以上是关于Python3.5修炼手册13的主要内容,如果未能解决你的问题,请参考以下文章

python3.5修炼手册12

python3.5修炼手册5

python3.5修炼手册11

Python3.5修炼手册7

python3.5修炼手册7

python3.5修炼手册9