目录
类和对象
在Python中,一切皆为对象。例如,所有字符串是str类的实例,所有列表是list类的实例等。尽管每个字符串的实际内容不同,但是他们的操作方法都是相同的,如取子串、定位、切片等。前面所学的str、int、float、list、turple、dictionary和set数据类型都是Python定义的内建类。这些类型的变量都是该数据类型的一个实例,即对象。
类由属性和方法组成,属性是描述对象特征的集合,方法是对象的行为。
1.1类的定义和对象的创建
Python中定义类的语法格式如下:
class 类名:
[类变量]
[def __init__(self,paramers):]
[def 函数名(self,…)]
注意:
- 直接定义在类体中的变量叫做类变量,是所有对象共享的变量,也称静态变量或静态数据,与所属的类对象绑定,不依赖于实例对象;
- 在类的方法中定义的变量叫做实例变量。类的方法的定义和普通函数的定义类似,但方法必须以self作为第一个参数。方法调用时,可以不传递self参数;
- 当创建对象时,self参数指向该对象。这样,当方法调用时,会通过self参数得知哪个对象调用了该方法;
- 实例方法必须绑定到一个实例对象上才能被调用;
- 当一个类定义完之后,就产生了一个类对象(与类名相同);
- 实例对象通过类名后跟圆括号来实例化;
- 在Python中,对象支持两种操作:引用和实例化。引用是通过类对象来调用类中的属性或方法;实例化是生成类对象的实例,也称实例对象,通过实例对象来引用类中的属性或方法。
简单的类定义,类的示例代码如下:
class fruit: #通过关键字class定义一个类
name='apple'
price=6.7
#定义一个实例方法,方法至少有一个参数self
def printName(self):
print(self.name)
print(self.price)
if __name__=='__main__':
print('通过类对象调用类变量:')
#通过类对象名.变量名来访问类变量
print('fruit.name={},fruit.price={}'.format(fruit.name,fruit.price))
print('通过实例对象调用方法和类变量:')
myFruit=fruit() #通过类名()的方式来初始化一个实例对象
myFruit.printName() #通过实例对象名.方法名来访问实例方法
#通过实例对象名.变量名来访问类变量
print('myFruit.name={},myFruit.price={}'.format(myFruit.name,myFruit.price))
print('修改类变量的值')
myFruit.name='pear' #可以在类外修改类变量的值
myFruit.price=3.5
print('myFruit.name={},myFruit.price={}'.format(myFruit.name, myFruit.price))
#不能通过类对象名.方法名来访问实例方法,下面的语句是错误的
#fruit.printName()
运行结果为:
E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
通过类对象调用类变量:
fruit.name=apple,fruit.price=6.7
通过实例对象调用方法和类变量:
apple
6.7
myFruit.name=apple,myFruit.price=6.7
修改类变量的值
myFruit.name=pear,myFruit.price=3.5
Process finished with exit code 0
1.2实例变量及封装
实例化之后,每个实例单独拥有的变量叫做实例变量。实例变量是与某个类的实例相关联的数据值,这些值独立于其他实例或类。当一个实例被释放后,这些变量同时被释放。
在Python中,实例变量的定义如下: self.变量
只要以self定义的变量都是实例变量。该变量可以定义在任何实例方法内。
实例变量的初始化最好通过定义__init__()或__new__()构造方法来完成。该方法在定义对象的时候自动调用。如果同时定义了这两种方法,优先调用__new__()方法来完成实例化。
调用实例变量有如下两种方式:
- 在类外通过变量直接调用;
- 在类内通过self间接调用。
Python中的封装,其实是使用构造方法将内容封装到对象中,然后通过对象直接或者通过self间接获取被封装的内容。
与其他语言不同的是,Python没有提供private、public这样的访问控制符,因此上述方式定义的实例变量在类外可以使用。
如果要实现真正的封装,让实例变量或方法成为私有的,需要在变量名和方法名前加双下划线,如__valueName或__functionName。
实例变量应用的代码如下:
class point1: #定义类point1
def __init__(self,x,y): #定义类point1的构造方法,创建对象时自动调用
self.x=x #创建实例变量x,并初始化
self.y=y #创建实例变量y,并初始化
def move(self,x,y): #定义实例方法,完成点的移动
self.x = x #类内通过self间接引用实例变量
self.y = y
def setX(self,x): #定义实例方法,设置x
self.x=x
def setY(self,y): #定义实例方法,设置y
self.y=y
def getX(self): #定义实例方法,获得x的值
return self.x
def getY(self): #定义实例方法,获得y的值
return self.y
def print(self): #定义实例方法,输出x和y
print('[',self.x,',',self.y,']')
#定义类point2,实例变量为私有
class point2:
def __init__(self,x,y): #定义类point2的构造方法,创建对象时自动调用
self.__x=x #创建私有实例变量__x,并初始化
self.__y=y #创建私有实例变量__y,并初始化
def move(self,x,y): #定义实例方法,完成点的移动
self.__x = x #类内通过self间接引用实例变量
self.__y = y
def setX(self,x): #定义实例方法,设置__x
self.__x=x
def setY(self,y): #定义实例方法,设置__y
self.__y=y
def getX(self): #定义实例方法,获得__x的值
return self.__x
def getY(self): #定义实例方法,获得__y的值
return self.__y
def print(self): #定义实例方法,输出__x和__y
print('[',self.__x,',',self.__y,']')
#print('hello',self.name)
#定义类hello
class hello:
def setName(self,yourName): #定义实例方法
self.name=yourName #name是实例变量
def print(self): #定义实例方法,使用了实例变量
print('hello',self.name)
if __name__=='__main__':
print('point1实例变量使用演示:')
p=point1(0,0) #自动调用构造方法__init()完成实例对象的创建
p.print() #调用实例方法,self参数不需要明确传递
p.move(4,5)
p.print()
p.x=10 #类外可以直接使用实例变量,格式为实例对象名.实例变量名
p.y=20
p.print()
p.setX(3.5)
p.setY(4.5)
print('p.x={},p.y={}'.format(p.x,p.y))
print('point2实例变量使用演示:')
q=point2(0,0) #初始化实例对象q
q.print()
q.move(4,5)
q.print()
#下面的语句不会报错,但是私有变量x和y不会被重新赋值
q.__x=10
q.__y=20
q.print()
q.setX(3.5)
q.setY(4.5)
print('q.x={},q.y={}'.format(q.getX(),q.getY()))
print('hello类的演示:')
h=hello()
h.setName('Mary')
h.print()
运行结果为:
E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
point1实例变量使用演示:
[ 0 , 0 ]
[ 4 , 5 ]
[ 10 , 20 ]
p.x=3.5,p.y=4.5
point2实例变量使用演示:
[ 0 , 0 ]
[ 4 , 5 ]
[ 4 , 5 ]
q.x=3.5,q.y=4.5
hello类的演示:
hello Mary
Process finished with exit code 0
关于类变量和实例变量的使用,还需要注意以下两点:
- 在类方法中引用的变量必定是类变量。而在实例方法中,当引用的变量名与类变量名相同时,实例变量会屏蔽类变量,即引用的是实例变量;若实例对象没有该名称的实例变量,引用的是类变量;
- 如果在实例方法中更改某个实例变量,并且存在同名的类变量,则修改的是实例变量;若实例对象没有与类变量同名的实例变量,会创建一个同名称的实例变量。此时若要修改类变量,只能在类方法中修改。
1.3方法
在Python类中定义的方法通常有三种:实例方法、类方法以及静态方法。以下是这三种方法的定义和调用方式。
1.实例方法
实例方法一般都以self作为第一个参数,必须和具体的对象实例绑定才能访问,即必须由对象调用。执行时,自动将调用该方法的对象赋值给self。
2.类方法
类方法必须以cls作为第一个参数。cls表示类本身,定义时使用@classmethod装饰器。可以通过类名或实例对象名来调用。执行类方法时,自动将调用该方法的类赋值给cls参数。
3.静态方法
静态方法不需要默认的任何参数,跟一般的普通方法类似,但是方法内不能使用任何实例变量,定义时使用@staticmethod装饰器。静态方法可以通过类名或实例对象名来调用。
方法的定义与应用的示例代码如下:
class foo:
animal='兔子'
def __init__(self,feature):
self.feature=feature
def print(self): #定义普通方法,至少有一个self参数
print('调用了普通方法:')
print('{}的特征是{}'.format(self.animal,self.feature)) #输出类变量和实例变量
@classmethod #类方法装饰器声明
def enemy(cls,name): #定义类方法,至少有一个cls参数,不能使用实例变量
print('调用了类方法')
enemyName=name #enemyName为类方法内的变量,类外不可用
#通过cls.类变量名的方法访问类变量(静态变量)
print('{}的天敌是{}'.format(cls.animal,enemyName))
@staticmethod #静态方法装饰器声明
def eat(name): #定义静态方法,无默认参数,不能使用实例变量
print('调用了静态方法')
eatname=name #eatname为静态方法中的变量,类外不可用
#通过类名.类变量的方法访问类变量(静态变量)
print('{}的食物是{}'.format(foo.animal,eatname))
if __name__=='__main__':
t=foo(['长耳朵','三瓣嘴','两颗大门牙','毛柔软','红眼睛']) #初始化实例对象
t.print() #利用实例对象调用普通方法
t.enemy(['狼','老鼠']) #通过实例对象调用类方法
foo.enemy(['黄鼠狼','狐狸']) #通过类名调用类方法
t.eat(['青草','胡萝卜','白菜','薯类']) #通过实例对象调用静态方法
foo.eat(['苹果','南瓜']) #通过类名调用静态方法
运行结果为:
E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
调用了普通方法:
兔子的特征是['长耳朵', '三瓣嘴', '两颗大门牙', '毛柔软', '红眼睛']
调用了类方法
兔子的天敌是['狼', '老鼠']
调用了类方法
兔子的天敌是['黄鼠狼', '狐狸']
调用了静态方法
兔子的食物是['青草', '胡萝卜', '白菜', '薯类']
调用了静态方法
兔子的食物是['苹果', '南瓜']
Process finished with exit code 0
1.4属性方法
python中的属性方法是普通方法的变种,即把一个方法变成静态属性。这样,方法在调用时就不用加小括号了。
属性方法定义有两种方式:
- 装饰器方式:定义方法时,使用@property装饰器;
- 静态属性方式:在类中定义值为property对象的静态属性。
属性方法格式为: property(方法名1,方法名2,方法名3,‘描述信息’)
参数说明:
- 方法名1:调用对象.属性自动触发执行方法;
- 方法名2:调用对象.属性=XXX时,自动触发执行方法;
- 方法名3:调用del 对象.属性时,自动触发执行方法;
- 描述信息:是一个字符串,调用对象.属性.__doc__。此参数是该属性的描述信息。
实际上,property()方法中的3个方法参数可以分别对应获取属性的方法、设置属性的方法和删除属性的方法。这样,外部对象就可以通过类似于访问变量的方式来达到获取、设置或删除类内属性的目的。
属性方法应用的示例代码如下:
class book: #定义book类
def __init__(self,bookname,author,price): #定义book类的构造方法
#分别初始化3个实例变量
self.__name=bookname
self.__author=author
self.__price=price
def getname(self): #定义普通实例方法
return self.__name
@property
def getauthor(self): #定义属性方法
return self.__author
def getprice(self): #定义普通实例方法
return self.__price
#定义静态属性,当执行对象名.PRICE时,触发getprice()方法的执行
PRICE=property(getprice)
def print(self): #定义普通实例方法
print('书名:{},作者:{},价格:{}'.format(self.__name,self.__author,self.__price))
class goods: #定义类goods
#定义类goods的构造函数,并初始化3个实例变量
def __init__(self,goodsname,produce,price):
self.__name=goodsname
self.__produce=produce
self.__price=price
def getname(self): #定义普通实例方法
return self.__name
def changename(self,name): #定义除self外,还有一个参数的方法
self.__name=name
def delname(self): #定义一个实例方法,删除一个实例变量
del self.__name
#定义静态属性
NAME=property(getname,changename,delname,'商品名称')
def print(self): #定义一个普通实例方法
print('商品名:{},生产厂家:{},价格:{}'.format(self.__name,self.__produce,self.__price))
if __name__=='__main__':
print('book类的演示:')
bk=book('python程序设计','张三',45) #初始化一个book类的实例对象bk
print(bk.getname()) #普通实例方法调用
print(bk.getauthor) #装饰器属性方法调用
print(bk.PRICE) #静态属性方法调用
bk.print()
print('goods类的演示:')
gds=goods('牛奶','三元',1.5) #初始化goods实例对象gds
print(gds.NAME) #自动调用第一个参数中定义的方法:getname()
gds.NAME='早餐奶' #自动调用第二个参数中定义的方法:changename()
del gds.NAME #自动调用第三个参数中定义的方法:delname()
#因删除了实例变量name,因此下面的调用会出错
#gds.print()
运行结果为:
E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
book类的演示:
python程序设计
张三
45
书名:python程序设计,作者:张三,价格:45
goods类的演示:
牛奶
Process finished with exit code 0
1.5类中的其他内置方法和属性
python类中还提供一些内置方法来完成特定的功能,如下表所示:
方法名或属性名 | 功能描述 |
__init__(self,…) | 构造方法。初始化对象,在创建新对象时调用 |
__del__(self) | 析构方法。释放对象,在对象被删除之前调用 |
__new__(cls,*args,**kwd) | 初始化实例 |
__str__(self) | 在使用print语句时被调用 |
__getitem__(self,key) | 获取序列的索引key对应的值,等价于seq[key] |
__len__(self) | 在调用内联函数len()时被调用 |
__cmp__(src,dst) | 比较两个对象src和dst |
__getattr__(s,name) | 获取属性的值 |
__setattr__(s,name,value) | 设置属性的值 |
__delattr__(s,name) | 删除name属性 |
__getattribute__() | 与__getattr__()类似 |
__call__(self,*args) | 对象后面加括号时触发执行,把实例对象作为函数调用 |
__iter__ | 用于迭代器 |
__gt__(self,other) | 判断self对象是否大于other对象 |
__lt__(self,other) | 判断self对象是否小于other对象 |
__ge__(self,other) | 判断self对象是否大于或等于other对象 |
__le__(self,other) | 判断self对象是否小于或等于other对象 |
__eq__(self,other) | 判断self对象是否等于other对象 |
__doc__ | 表示类的描述信息 |
__module__ | 表示当前操作的对象在哪个模块 |
__class__ | 表示当前操作的对象的类是什么 |
__dict__ | 类或对象中的所有成员 |
内置方法应用的实例程序如下:
class fruit:
def __init__(self,name,price):
self.name=name
self.price=price
#此方法一般无须定义。析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
def __del__(self):
pass
def __call__(self, *args, **kwargs): #对象()或者类()触发执行call()方法
print('fruit {} is tasteful.'.format(self.name))
def __str__(self):
return 'the price of '+self.name+' is '+str(self.price)
def __getattribute__(self, name): #获取属性的方法
return object.__getattribute__(self,name)
def __setattr__(self, name, value): #设置属性的方法
self.__dict__[name]=value
if __name__=='__main__':
print('fruit.__doc__:{}'.format(fruit.__doc__))
obj=fruit('apple',5.5)
print('obj.__module__:{0}'.format(obj.__module__)) #输出模块
print('obj.__class__:{0}'.format(obj.__class__)) # 输出类
print('obj.__dict__:{0}'.format(obj.__dict__)) # 输出类的成员
obj() #执行call()方法
print(obj) #如果定义了__str__()方法,则调用它
obj.__dict__['_fruit__price']=6.5 #设置price属性
print(obj.__dict__.get('_fruit__price'))
运行结果为:
E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
fruit.__doc__:None
obj.__module__:__main__
obj.__class__:<class '__main__.fruit'>
obj.__dict__:{'name': 'apple', 'price': 5.5}
fruit apple is tasteful.
the price of apple is 5.5
6.5
Process finished with exit code 0
1.第12行和第24行有些不太明白,感觉第12行的函数像递归调用,第24行中把_fruit__price改成price之后运行结果是一样的