第一章:python
第14节:类
1,类与对象,类的简单介绍
1)类表示一类实物的集合,类与实物的相互关系表示为,类是对象的定义,而实物存放了类中所定义的对象的信息。
2)面向对象编程的设计思路为:
····1> 确定对象的属性和方法
····2> 抽象具有共同特征的对象为一类
····3> 设计类和类之间的关系
····4> 实例化对象
3)类存在它的属性和方法:
属性(在类之外即为变量)表示对象的特征
方法(在类之外即为函数)表示对象的操作
4)如何创建一个类:
类的首字母要大写,而函数(或者是类当中的方法)的首字母要小写
用class来定义一个类,写法为:
>>> class MyData(object):
#MyData首字母大写,其他字母可以小写或大写
>>> ····pass
5)类即其中的属性和方法的一些事宜
类的命名要体现事物的过程及用途
属性命名要有数据值名字的特点
方法名应当指出对应对象或值的行为
6)类的实例化应用
比如下面的例子:
>>> class Car(object):
#首字母大写定义一个类
>>> ····"""类的描述信息"""
#类的描述信息
>>> ····color='black'
#定义一个类的属性
>>> ····def run(self):
#定义一个类的方法
>>> ········"""方法"""
#方法的描述信息
>>> ········print 'go!'
#方法的内容
>>>
>>> BMW=Car()
#类的使用:创建一个类的对象实例,即类的实例化
>>> BMW.color='red'
#访问类的属性,也可对类的属性进行重新赋值
>>> BMW.run()
#访问类的方法
事项:在访问类的属性时不加括号,访问类的方法时加括号
2,类的属性
类存在三种属性:
1)公有属性(类被实例化后均可使用)。写法为:名字
;例如:name=‘zhengzhou’
2)私有属性(类实例化后在外部无法访问,通常在类的内部,用于传值使用)。写法为:__名字
(前面两个下划线加名字);例如:__number=10000
3)内置属性(或称为特殊属性)。写法为:__名字__
(前后两个下划线加名字);例如:__str__
代码实例:
class Human(object):
····name='zhangsan'
····__money=800
zhangsan=Human
zhangsan.__money
Traceback (most recent call last):
··File "<stdin>", line 1, in <module>
AttributeError: type object 'Human' has no attribute '__money'
类实例化后其公有属性name可以访问,而私有属性__money不可以访问
3,决定类的属性
要知道一个类的所有属性,可通过两种方法:
1)利用内建函数dir()
2)访问类的字典属性__dict__
例如查看python自带的列表(list)的所有属性
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
或者
>>> list.__dict__
dict_proxy({'__getslice__': <slot wrapper '__getslice__' of 'list' objects>, '__getattribute__': <slot wrapper '__getattribute__' of 'list' objects> ··· ··· })
二者区别在于dir()
返回的仅仅是对象的属性的一个名字列表,而__dict__
返回的是一个字典,它的键(key)是属性名,值(value)是相应的属性对象的数据值。
4,特殊的类属性
对于任何类C,其所有特殊属性如下:
表13.1
特殊类属性
| C.__name__
| 类C的名字(字符串) | #名字
| C.__doc__
| 类C的文档字符串 | #文档
| C.__bases__
| 类C的所有父类构成的元组 | #元组
| C.__dict__
| 类C的属性 | #属性
| C.__module__
| 类C定义所在的模块 | #模块
| C.__class__
| 实例C对应的类 | #实例
例如如下的类:
>>> class MyClass(object):
... ····'MyClass class definiton'
... ····myVersion = '1.1'
... ····def showMyVersion(self):
... ········print MyClass.MyVersion
...
#例子当中定义了一个类,有类的描述信息,类的属性和方法
>>> MyClass.__name__
#返回的结果是给出类的字符名字,返回一个字符串
'MyClass'
>>> MyClass.__doc__
#返回的结果该类的描述信息,返回一个字符串
'MyClass class definiton'
#__doc__
是类的文档字符串,与函数及模块的文档字符串相似,但必须紧随头行(header line)后面的字符串。文档字符串不能被派生类继承,派生类必须含有它们自己的文档字符串。
>>> MyClass.__bases__
#返回所有父类构成的元组,当然该类没有父类,或者说父类只是object,如果是class MyClass(): 则返回一个空元组
(<type 'object'>,)
>>> print MyClass.__dict__
#返回一个字典,包含类的所有数据属性
{'__module__': '__main__', 'showMyVersion': <function showMyVersion at 0x7f235c972b90>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'MyClass class definiton'}
>>> MyClass.__module__
'__main__'
>>> MyClass.__class__
<type 'type'>
4,类的方法
类的方法也分为两种:
1)公有方法,def funName(self): 该方法实例化后可以使用
2)私有方法,def __funName(self): 该方法实例化后外部不可以使用,仅在类的内部可以使用
例如:
>>> class Human:
... ····name='zhengzhou'
... ····__age=22
... ····def say(self):
#定义公有方法,方法传入的第一个参数为self,self指代的是对象本身,即实例化对象
... ········print 'My name is %s I am %d years old'%(self.name, self.__age)
#公有方法的内容,当然如果想在方法当中使用该类的类属性,则类属性在方法中被调用时需要加self,即self+.+属性名称,例如self.name和self.__age,类的私有方法在类实例化时无法直接访问,但可以通过调用类的方法来查看类的私有属性
... ····def __lie(self):
#定义私有属性
... ········print 'I am 23'
...
>>> zhangsan=Human()
#类的实例化
>>> zhangsan.say()
#调用类的公有方法
My name is zhengzhou I am 22 years old
#输出了两个类的属性的值,zhengzhou和22
>>> zhangsan.__lie()
#在调用类的私有方法时会报错
Traceback (most recent call last):
··File "<stdin>", line 1, in <module>
AttributeError: Human instance has no attribute '__lie'
#报错的信息内容
4,类的特殊方法,即__init__()
“构造器”方法
__init__()
该方法叫做构造函数,其在类的实例化时自动被调用
其主要作用是,该方法实现对象的初始化作用,当类被实例化时,可以直接进行一些操作,主要用于类实例化后的外部接收传值
>>> class Human:
... ····def __init__(self, name, gender, age):
#构造函数要接收4个传值,其中实例对象作为第一个参数(self)被传递给构造函数,另外name, gender, age表示实例化时的传值
... ········self.name=name
... ········self.gender=gender
... ········self.age=age
>>> zhangsan=Human('zhangsan', 'male', 30)
#所以类Human在实例化时接受三个外部传值,并将外部传值赋给实例属性
>>> zhangsan.name
#调用实例属性
'zhangsan'
>>> zhangsan.gender
'male'
>>> zhangsan.age
30
类在实例化时直接执行构造函数(__init__
)当中的内容
>>> zhangsan=Human()
#如果类在实例化时没有给构造函数(__init__
)足够传参则会报错
Traceback (most recent call last):
··File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 4 arguments (1 given)
#返回的报错信息
__init__()
应当返回None
如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。__init__()
就不应当返回任何对象(应当为None);如果返回非None的任何其他对象都会导致TypeError异常;
>>> class MyClass:
... ····def __init__(self):
... ········print 'initialized'
... ········return 1
#所以构造函数内不应该有return语句
...
>>> mc = MyClass()
initialized
Traceback (most recent call last):
··File "<stdin>", line 1, in <module>
TypeError: __init__() should return None
5,类属性、实例属性和类实例化时构造函数的传值的区别
类属性仅是与类相关的数据值,和实例属性不同,类属性和类是否实例化无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。
访问类属性,可以用类名加属性名方法访问,也可以用实例化对象加属性名方法访问
1)类属性和实例属性
>>> class C():
#定义一个类C
... version = 1.2
#类属性version其值为1.2
...
>>> c = C()
#类实例化
>>> C.version
#通过类名加属性名访问类属性
1.2
>>> c.version
#实例化对象加属性名访问类属性
1.2
#二者得到的结果相同
>>> C.version += 0.1
#通过类(只能这样)类更新类属性的值
>>> c.version
1.3
>>> C.version
1.3
#无论哪种方式都改变了类属性的值
>>> c.version += 0.1
#通过实例化对象更新值
>>> C.version
#类属性的值没有变化
1.3
>>> c.version
#而实例属性的值发生了变化
1.4000000000000001
类属性更新值改变类属性也改变实例化后的属性,实例属性更新值改变实例属性不改变类属性
1)类属性和类实例化时构造函数的传值
>>> class Human:
... ····name='ren'
... ····age=25
... ····def __init__(self, name, age):
... ········print "#"*50
... ········self.name=name
#将外部传入name值赋给类当中name属性
... ········self.age=age
#将外部传入age值赋给类当中age属性
...
>>> zhangsan=Human('zhangsan', 30)
#Human这个类的实例化,及在初始化是对于__init__
构造器的传值,name=’zhangsan’,age=30
##################################################
>>> zhangsan.name
'zhangsan'
>>> zhangsan.age
30
#通过实例化的传值,实例属性的值改变
>>> Human.name
'ren'
>>> Human.age
25
#而无论外部传值是什么,类属性的值永远不变
构造函数传值不改变类属性
6,属性的知识拓展
class MyClass:
····var1="类属性,也叫静态属性"
#可以在对象当中访问,也可在类当中直接访问
····__var2="类的私有属性"
#只能在类当中相互调用
····def __inti__(self):
········self.var2="var2是实例的属性"
#将要实例化出对象的属性
········self.__var3="实例的私有属性"
#对象的私有属性
········var4="简单变量,非属性,作用域只在该方法内"
#局部变量,只在函数体当中起作用
7,构造器传值与类方法传值
1)如果方法在类实例化后,需要调用时需要传值,则在方法调用时也需要传值
>>> class Type:
... ····def __init__(self, zhengshu, zifuchuan):
... ········self.zhengshu=zhengshu
... ········self.zifuchuan=zifuchuan
... ····def show_number(self, zhengshu):
... ········print "shu chu %d"%self.zhengshu
#注意这两处输出的都是Type这个类在实例化时的传值
... ····def show_str(self, zifuchuan):
... ········print "shu chu %s"%self.zifuchuan
#注意这两处输出的都是Type这个类在实例化时的传值
>>> result=Type(20,'jieguo')
>>> result.show_number(30)
#所以尽管在调用类的事例化方法时,传入的参数与实例化时的参数不同,但调用方法输入的结果仍然是实例化时传值的结果
shu chu 20
>>> result.show_number('90')
shu chu 20
2)如果想输入调用方法时的传值结果,则应该写成如下的格式
>>> class Type:
... ····def __init__(self, zhengshu, zifuchuan):
... ········self.zhengshu=zhengshu
... ········self.zifuchuan=zifuchuan
... ····def show_number(self, zhengshu):
... ········print "shu chu %d"%zhengshu
#注意这两处的不同在于输出的结果是zhengshu而不是self.zhengshu,即输出外部传值而不是实例化时的传值
... ····def show_str(self, zifuchuan):
... ········print "shu chu %s"%zifuchuan
#注意这两处的不同在于输出的结果是zifuchuan而不是self.zifuchuan,即输出外部传值而不是实例化时的传值
>>> result=Type(20,'jieguo')
#实例化传值和上面相同
>>> result.show_number(30)
#但这里在调用方法时的传值和实例化时的传值不同,输出的结果是调用方法时的传值;调用方法时传值30,实例化的传值是20
shu chu 30
>>> result.show_str('name')
#这里的结果同上
shu chu name
>>> result.show_str()
#当然传值数量不够也会报错
Traceback (most recent call last):
··File "<pyshell#15>", line 1, in <module>
····result.show_str()
TypeError: show_str() takes exactly 2 arguments (1 given)
3)如果想直接使用类当中的方法,可以不需要构造器
>>> class Type:
#不需要构造器,直接加入方法
... ····def show_number(self, zhengshu):
... ········print "shu chu %d"%zhengshu
... ····def show_str(self, zifuchuan):
... ········print "shu chu %s"%zifuchuan
...
>>> result=Type()
>>> result.show_number(90)
shu chu 90
>>> result.show_number('10')
#同时注意输入参数的类型,需要对应,否则会报错
Traceback (most recent call last):
··File "<pyshell#20>", line 1, in <module>
····result.show_number('10')
··File "<pyshell#17>", line 3, in show_number
····print "shu chu %d"%zhengshu
TypeError: %d format: a number is required, not str
>>> result.show_str('name')
shu chu name
总结:类当中构造器的所用在于,将类实例化时传入的参数,变为实例化参数,而该实例化参数则能够应用到整个类当中;而在调用类的方法时的传值,该传值仅仅能够应用于该方法,不能够应用于类的其他方法,更不能成为类的实例化属性,除非通过一些特殊操作。
8,三个内置装饰器,或称为函数修饰符
1)staticmethod:类静态方法
作用:与成员方法的区别在于编写方法是不需要self参数,并且可以在类不进行实例化的情况下直接调用;
代码实例:
>>> class Human:
... ····name='ren'
... ····def __init__(self, name, age):
... ········self.name=name
... ········self.age=age
... ····@staticmethod
#加入staticmethod这个函数修饰符
... ····def bye():
#既然加入了函数修饰符,则bye这个函数不需要self这个参数了,同时也将其变为一个静态方法
... ········print 'GAME OVER!'
...
>>> Human.bye()
#所以在加入函数修饰符以后,可以直接用类访问staticmethod修饰过的方法
GAME OVER!
>>> tom=Human('name', 20)
#如果用实例化方法访问,需要先对类进行实例化,并传参
>>> tom.bye()
#之后再访问对象方法
GAME OVER!
2)classmethod:类方法
类方法与成员方法区别在于其所接收的第一个参数不是self,而是cls(当前类的具体类型);简单来讲,对于类方法而言,需要类作为第一个参数,而非self这个实例,它是由解释器传给方法。类不需要特别地命名,类似self,不过很多人使用cls作为变量名。
例如:
>>> @classmethod
>>> def say(cls):
#当然括号当中写self也无所谓
... ····print 'my name is %s and I have %d'%(self.name, self.__money)
classmethod用法和staticmethod用法类似,但正常来说,方法中可以有self,而staticmethod是没有self的
这两种修饰器的特点是,加入之后类可以直接调用方法,而无需进行实例化
3)property:属性方法
属性方法的作用在于,其可将一个类方法转变成一个类属性,变为只读属性
先看一个没有属性方法的实例:
>>> class Human:
... ····name='ren'
... ····@classmethod
... ····def say(self):
... ········print 'my name is %s'%self.name
... ········return self.name
#对于类方法做一个return
...
>>> tom=Human()
#实例化,实例化时虽然不需要传参,但是在实例化时需要加括号
>>> x=Human.say()
#将say方法中的return值交给x
my name is ren
>>> print x
#打印出的结果是Human当中的属性name
ren
>>> tom.say()
#调用实例化当中的say方法
my name is ren
'ren'
>>> tom.say
#但是如果按照实例化属性的方式调用方法会出现下面结果
<bound method classobj.say of <class __main__.Human at 0x024EE9D0>>
如果加入@property这个属性方法,则可以将方法直接转变为属性
>>> class Human:
... ····name='ren'
... ····@classmethod
... ····def say(self):
... ········print 'my name is %s'%self.name
... ········return self.name
...
>>> jam=Human()
#实例化方法同上
>>> jam.say
#加入了属性方法,这时实例化后的say已经是一个类属性而非方法了
my name is ren
'ren'
>>> x=jam.say
可以将return值赋给一个变量
my name is ren
>>> x
'ren'
下面则简要说明property属性方法加入前后的区别
>>> class Person:
... ····def __init__(self, first_name, last_name):
... ········self.first_name=first_name
... ········self.last_name=last_name
... ····def full_name(self):
... ········return "%s %s"%(self.first_name, self.last_name)
>>> full=Person('hua','sheng')
>>> fullname=full.full_name()
>>> fullname
'hua sheng'
>>> full.full_name
<bound method Person.full_name of <__main__.Person instance at 0x02D214B8>>
>>> class Person:
... ····def __init__(self, first_name, last_name):
... ········self.first_name=first_name
... ········self.last_name=last_name
... ····@property
... ····def full_name(self):
... ········return "%s %s"%(self.first_name, self.last_name)
>>> full=Person('hua','sheng')
>>> fullname=full.full_name()
Traceback (most recent call last):
·File "<pyshell#82>", line 1, in <module>
··fullname=full.full_name()
TypeError: 'str' object is not callable
>>> full.full_name
'hua sheng'