Python中使用类(class〕来实现面向对象编程。Python中的类, 具有面向对象编程的所有基本特征:允许多继承、派生类可以重写它父类的任何方法、方法可以调用父类中同名的方法, 对象可以包含任意数量和类型的数据成员。
创建类
Python中, 使用class语句来创建一个新类。class之后为类的名称, 并以冒号结尾。
类定义会创建一个新的命名空间作为一个局部的作用域。在该作用域中的变量被叫作成员变量。在该类作用域下的函数就被叫作成员函数, 也叫作该类的方法。当一个类定义结束后, 系统就会创建一个类对象(ClassObject)。
创建类属性
1、定义类属性
需要注意的是, 在定义类方法时, 需要有一个默认的形参一self。而在调用类方法时, 却不需要传入形参self所对应的实参。因为这个形参self是由类的内部机制调用的。
class MyClass :
""" A simple example class """
i = 12345
def f(self):
return ' I love Python '
2、使用类
创建好的类可以实例化成一个对象, 井通过调用对象的方法来实现具体功能。
myc = MyClass()
print(myc.i)
print(myc.f())
3、类的内置属性
Python中的类有一些相同的内置属性。这些内置属性用于维护类的基本信息。
__name__:类名称。
__doc__:类的文档字符串。
__module__:类定义所在的模块。如果是直接运行当前文件, 该值为__main__
__base__:该类所有的父类<class『object'>列表, 是一个阳ple类型的对象。
__diet__:该类的所有属性(由类的数据属性组成), 是一个diet类型的对象。
定义类的动态属性
Python中的类, 在使用时能够根据需要随时添加动态属性。
添加动态属性
为类添加动态属性的方法是:在实例化对象的后面加一个点, 在点后跟上属性名称。
如果该对象己经有这个属性, 系统就会读取其内容;否则, 系统会自动为该对象加上这个属。
class Record :
pass
Anna = Record() #实例化一个对象
Anna.name ='Anna ' #为对象添加属性并赋值
Anna.age = 42
print(Anna.name, Anna.age)
删除动态属性
虽然可以动态地添加属性, 但总不能无止境地增加很多属性。在有时还需将不用的属性删除。用de!加上对象属性, 即可删除属性。
del Anna.age
print(Anna.age)
限制类属性(__slots__)
__slots__是一个特殊的变量, 其值可以是元组的形式, 元组的元素为该类所允许添加的属性名称。在一个类中, 一旦为_slots_赋值, 则该类的实例化对象就只能添加__slots__中所规定的属性。
class Record :
__slots__ = ('name','age')
Anna = Record() #实例化一个对象
Anna.name ='Anna ' #为对象添加属性并赋值
Anna.age2 = 42 #系统报错
实例化类对象
带有初始值的实例化
实例化类,可以使用类的名字加括号来实现。对于复杂功能的类,实例化的同时需要为其初始化一些必须的成员变量,即带有初始值的实例化。
类的初始化方法(__init__)
要想实现带有初始值的实例化, 需要在定义类时, 在类里面实现一个__init__方法。__init__方法的定义与函数几乎一样, 也需要形参, 并且支持形参默认值等规则。
class MyClass: #定义一个类
def __init__(self,name,age): #定义该类的初始化函数
self:name=name
self.age=age
def getrecode(self): #定义一个成员函数
return self.name,self.age
myc=MyClass("Anna",42)
print(myc.getrecode()) #调用对象的成员函数, 并将返回值打印。输出元纽类型:(Anna’,42)
隐藏调用类的初始化方法
每个类实例化时,都会调用内部默认的__init__方法,如果在类中实现了__init__方法,则会优先调用自己的__init__方法。即使没有初始值也会调用。
注意:如果类中的__init__函数, 有除self以外的参数。实例化该类时, 就必须输入与__init__函数应的参数, 否则就会错。
class中的self
在类中,可以使用self关键字指定本身。类中的成员函数必须第一个参数是self,以保证实例化的对象能调用自己的成员函数。
1、直接调用类的成员函数:类名.方法
从函数传值的角度来看, 直接使用该类的成员函数并传入实例化对象, 与通过“类对象.成员函数”的调用方法效果一样。
2、将成员函数定义在外面
如果将类中的成员函数(即方法)看成一个函数, 则完全可以把类的成员函数定义在类外面。但是, 第一个参数是self这个规则还要必须保留。例如:
def fun(self): #在类的外面定义方法
return 'IlovePython'
class MyClass:
i=12345
f=fun #在类的内部指定成员函数
myc=MyClass() #实例化类对象, 并赋位给myc
print(myc.f()) #调用类实例myc的成员函数f, 并打印返回值。输出:I love Python
3、类内的成员互相访问
想在类中使用该类的其它的成员变量或方法,需要使用self加点的方式调用
在Python中, 每个值都是一个对象, 可以通过object.__class__来获取对象的class(即类型), 其作用与type()相同。
类方法(@classmethod)与静态方法(@staticmethod)
类方法
在一个类的定义中,如某个方法使用了装饰器@classmethod进行装饰,则说明该方法是一个类方法。
类方法与默认成员方法的区别是:类方法属于类;而默认成员方法属于类实例化对象。具体表现为:
类方法的第一个参数必须是cls(用来指代该类), 而默认成员方法的第一个参数必须是self(用来指代该类的实例对象)。在调用类方法, 需要使用“类名.类方法名”的方式。
在调用默认成员方法时, 则是使用“该类的实例化对象.类方法名”的方式。
class MyClass:
def f(self): #默认的成员方法
return 'I love Python'
@classmethod
def fcls(cls): #定义类方法
return '类方法:I love Python'
myc=MyClass()
print(myc.f())
print(MyClass.fcls())
静态方法
静态方法的定义是使用装饰器@staticmethod进行装饰的。静态方法的特点是:第一个参数没有任何要求, 与普通的函数参数一样。其调用方式更加宽松, 使用类名或是类实例化对象都可以对其进行调用。代码如下:
class MyClass :
@staticmethod
def fstatic( ) :
return '静态方法:Ilove Python '
类变量与实例变量
变量与实例变量的区别:
类变量:是指类的属性和方法, 类似于一种静态数据。类变量是在定义类时所定义的, 类的所有实例都会共享类变量;
实例变量:更像是一种动态数据。只有在实例化时, 系统才会为该实例指定它特有的数据, 有别于该类中的其他个体。
通过修改一个类的类变量, 可以将这个类的所有实例化对象的属性统一修改。但是, 当实例变量与类变量同名时, 系统会以实例变量优先。这会导致类变量失效, 从而无法批量修改实例化后的类属性。
销毁类实例化对象
调用__del__方法可以销毁类对象。在销毁对象时,会自动调用__del__方法。
类变量的私有化属性
以两个下划线开头的成员对象为私有化成员
私有化的实现方式是:给类变量加上一个私有化(private)权限。被私有化的属性不能被该类的实例化对象直接访问, 但是类的内部成员函数是可以访问的。如果类的实例化对象想要取得该类的私有化属性, 可以通过调用该类中的get函数来完成。
与私有化(private)对应的是公有化(public)。公有化是Python中的默认权限, 即, 所有的对象都可以访问。
使用装饰器技术实现私有化(@property)
私有化属性的应用提高了程序的健壮性。但必须为私有化变量封装get方法, 通过调用get方法才可以取值。
1、装饰器实现私有化变量的get函数
在get函数上方使用@property修饰后,可以把改get函数当成属性来用,即:对象.get函数名
2、装饰器实现私有化变量的set函数
函数Occupation是私有变量__Occupation的set函数。通过@Occupation.setter装饰之后, 就可以当作属性被调用了
子类继承
类的继承分为单继承和多继承。
单继承是指派生类只有一个父类。即, 只继承了一个父类的属性及方法。
多继承是指, 派生类有多个父类。即, 子类继承了多个父类的属性及方法。
单继承的实现
单继承的实现非常简单定义类时, 在类名后面加一个括号, 在括号里指定父类的类名。
访问派生类的属性时, 首先会在当前的派生类中搜索, 如果没有找到, 则会递归地去基类中寻找。
当子类的方法与父类的方法同名时, 父类的方法将失效。即, 子类方法覆写(override)了父类的方法。
多重继承的实现
多重继承与单继承类似, 只不过是在类名后的括号里多加几个父类, 间用逗号分割。
多重继承下, 若要访问派生类的属性, 默认的搜索的规则是:深度优先, 从左到右。即:
(1)如果一个属性在当前类中没有被找到, 它就会搜寻FatherClassNamel。
(2)如果FatherClassName1中没找到, 就会递归地搜寻Fath巳rClassNamel的父类。
(3)如果FatherClassName1的所有父类没找到, 就会搜索FatherClassName2。
(4)循环(2)、(3)两步, 依次类推, 直到找到为止。
(5)如果都没找到就会报错。
super函数
用于在有类继承关系且有方法覆盖的子类中,调用父类中被覆盖的方法。
通过super函数进行调用的父类方法, 会保证只执行一次。
注意:使用super函数时, 对父类的方法调用会自动传入self。无需再传入self, 否则会报错误。
类相关的内置函数
判断实例(isinstance)
函数isinstance的作用, 判断某个实例对象是否是某个类或其派生类的实例。
定义:isinstance(object, class_name)
object:实例对象
class_name:类名
如果object(object.__class__)是类class_name或其派生类的实例, 则返回True。
判断子类(issubclass)
函数issubclass的作用是, 判断类是否是另一个类的子类。
定义:issubclass(classl,class2)
如果类classl是class2的派生类, 则返回True。例如issubclass(bool,int)会返回True, 因为booI是int的派生类。
判断类实例中是否含有某个属性(hasattr)
函数hasattr的作用是, 判断类实例中是否含有某个属性。
定义:hasattr(obj, name, /)
如果类实例。bj中含有name属性, 则返回True:否则返回False。
获得类实例中的某个属性(getattr)
函数getattr的作用是, 获得类实例中的某个属性。
定义:getattr(obj, name[, default])
如果类实例obj中含有name属性, 则返回该属性的值:否则看是否有default, 如果有, 则将该default的值返回:否则会产生一个AttributeError的异常。
设置类实例中的某个属性值(setattr)
函数setattr的作用是, 设置类实例中的某个属性。
定义:setattr(obj , name , value , /)
为实例obj中的name赋上value的值, 如果obj中没有name, 则新建一个。
其存在的价值在于, 与getattr函数混合使用, 如:getattr(obj,name,setattr(obj,name,value,/))
该语句的作用是, 先为o句中的name赋值value(如果obj中没有name, 就创建一个, 再赋值), 接着再取obj中的name属性。
包装与代理
包装就是将某个对象(或类), 进行重新打包, 转换成另外一种更适合当前使用场合的对外接口。被包装后的对象(或类〉, 自身的内部逻辑并没有改变, 只是变化了对外的接口而己。这么做的初衷, 是为了让对象(或类〉更能适应当前的调用环境。
在Python中包装的具体做法是:
(1)创建一个类, 将其要包装的目标类的对象, 作为新创建类的成员对象。即, 包装类包含被包装类的实例。
(2)为包装类实现各种与外部调用相吻合的接口。通过接口将调用关系传递到被包装类对象中, 以实现真正的包装效果。
class WrapMe(object):
def __init__(self, obj) : #初始化函数, 将被包装对象传入
self.__data = obj #__data为包装类的成员对象
def get(self ) :
return self.__data #返回被包装类中的数据, 这里是返回整个对象了
def __str__(self) : #重载str函数
return "data =" + str (self.__data)
mynum = WrapMe(888) #对一个整型类的实例888进行包装, 并实例化成mynum对象
print(str(mynum)) #调用了包装类对象的str。输出:data=888
还可以使用代理的方式来实现类的包装。具体做法是, 将包装类变成一个属性的“代理。这使得访问包装类的属性等同于访问被包装类的属性。