类和实例
一、面向对象和面向过程
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行;为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递
二、类和实例
基本上来将python中的面向对象编程的实现和java中是很相似的
1、一个类的封装
类名后面的object代表继承的类,这里的和java中的object是很相似的概念
init方法和java中的构造方法类似,就是用来初始化类的对象的,其中的参数self表示创建的实例本身和java中的this很类似
值得注意的是python类中包括init方法在类的所有方法第一参数都是self,只是调用方法时不用传递这个参数,python解析器自动帮我们完成了
和其他的脚本语言类似的是python也可以动态地为对象添加属性,这样就导致了对于同一个类的两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同
class Student ( object ) :
def __init__ ( self, name, score) :
self. name = name
self. score = score
stu1 = Student( 'allen' , 78 )
stu1. haha = "test-haha"
print ( stu1. name)
print ( stu1. haha)
三、数据封装
面向对象编程的一个重要特点就是数据封装
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据
通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节,实现了数据和逻辑的封装
class Student ( object ) :
def __init__ ( self, name, score) :
self. name = name
self. score = score
def get_grade ( self) :
if self. score >= 90 :
return 'A'
elif self. score >= 60 :
return 'B'
else :
return 'C'
访问限制
访问的限制基本来讲和java差不多,就是把变量私有化,对外提供访问和修改的get和set方法
但是有点不同的是,python并没有十分强硬的措施阻止你访问私有的数据,全靠自觉了
一、三种变量
__name__
类似这样的特殊变量,特殊变量是可以直接访问的,不是private变量
以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但按照约定还是把它视为private就好了
__name
这样的变量是私有变量,直接访问会出错,需要提供get/set方法;但实际上直接访问也是能够实现,python对这种变量的私有化的实现是通过修改变量名来实现的,也就是说你实际看到的__name
被解析器修改了名字,一般的解析器是把名字修改为这样的_Classname__name
,但是最好不要这么做,不同的解析器的实现可能是不同的,而且约定了不要这么做的啦
class Student ( object ) :
def __init__ ( self, name, gender) :
self. name = name
self. __gender = gender
def get_gender ( self) :
return self. __gender
def set_gender ( self, gender) :
self. __gender= gender
继承和多态
一、继承
继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写
和iava不一样的是,java中一个方法接受一个父类参数,必须要求方法调用的时候传入的参数是这个父类或者其子类的对象;但是对于python这种动态语言,我不管你的继承关系是什么,只要你有相应的方法即可,这就是所谓的鸭子类型特点,只要看起来像并不一定要是,这也决定了python的继承不像静态语言那样是必须的
二、多态
所谓的多态简单来说就是:我有一个方法,方法接受一个类型参数,调用方法的时候,你可以传递该类型的对象,也可以传递该类的子类对象,最终的效果是,不论是父类还是子类对象,方法都可以正常运行,并且因为传入对象的不同表现出不同的运行结果
多态真正的威力在于:调用方只管调用,不管细节,而当我们新增一种子类时,只要确保相应方法编写正确,不用管原来的代码是如何调用的,这就是著名的开闭原则 : 对扩展开放:允许新增子类 对修改封闭:不需要修改依赖父类的函数
获取对象信息
一、使用type
>> > type ( 123 ) == type ( 456 )
True
>> > type ( 123 ) == int
True
>> > type ( 'abc' ) == type ( 123 )
False
判断基本数据类型可以直接写int,str等,要判断一个对象是否是函数可以使用types模块中定义的常量
>> > type ( fn) == types. FunctionType
True
>> > type ( abs ) == types. BuiltinFunctionType
True
>> > type ( lambda x: x) == types. LambdaType
True
>> > type ( ( x for x in range ( 10 ) ) ) == types. GeneratorType
True
二、使用isinstance
isinstance()
判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上
能用type()判断的基本类型也可以用isinstance()判断,总是优先使用isinstance()判断类型,可以将指定类型及其子类一网打尽
还可以判断一个变量是否是某些类型中的一种:isinstance([1, 2, 3], (list, tuple))
三、使用dir
如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list
类似__xxx__的属性和方法在Python中都是有特殊用途的,比如python内置函数len(obj)
实际上是调用对象自己的__len__()
函数来实现的
通过getattr()、setattr()、hasattr()
三个方法可以直接操作一个对象的状态(方法和属性)
setattr ( obj, 'y' , 19 )
hasattr ( obj, 'y' )
getattr ( obj, 'y' )
getattr ( obj, 'z' , 404 )
实例属性和类属性
以下的代码中 name1 是类属性,name 是实例属性
实例属性属于各个实例所有,互不干扰
类属性属于类所有,所有实例共享一个属性
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误
class Student ( object ) :
name1 = 'Student'
def __init__ ( self, name) :
self. name = name