面向对象编程
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()函数可以判断一个对象是否为"可调用"对象
转载于:https://blog.51cto.com/duyuheng/1966068