python程序支持两种编程方法:面向过程编程和面向对象编程。主要讨论面向对象编程。
一 定义类
类是面向对象的基础,在python中使用class来定义类,类主要由属性和方法来构成。
二 定义属性
属性也称为成员变量,可以是任意数据类型。属性分为类属性(类变量)和实例属性(实例变量)两种。类属性在整个实例化的对象中是公用的,它定义在类中且在所有 方法之外。实例属性是对于每个实例都独有的数据。
属性也分为公有属性和私有属性。私有属性名称以前加两个下划线__开头,私有属性在类外部无法直接访问。
在类的一个方法中可以引用该类的任意属性,类属性的引用方式为“类名称.类属性名称”,其他属性的引用方式是“self.属性名称”。
class A:
x = 2 # 类属性x
__y = 0 # 私有类属性y
def __init__(self, p): # 构造方法
self.z = p
self.__w = 0
def add(self):
self.__y += 10
self.__w += 20
def disp(self):
print(A.x, self.__y, self.z, self.__w)
A.x += 1
a = A(10) #和c语言中的结构很相似
a.add()
a.disp()
b = A('Bye')
b.disp()
三 定义方法
方法也称为成员函数,用来表示对象的行为。在类的内部使用def来定义一个方法,与一般函数的定义不同,类方法必须包含一个参数self,且为第一个参数。self代表的是类的实例,即当前对象的地址,而self.claa指向类。注意,self不是python关键字。
在类的一个方法中可以调用该类的任意方法,调用方式是“self.方法名称(参数)”。
类有一个名称为__init__()的专门方法。该方法的作用是在实例化一个对象的同时给该对象的属性赋值。其他方法如下:
__del__ #析构函数,在释放对象的时候使用。
__repr__ #在输出时实现转换。
__setitem__ #按照索引赋值。
__getitem__ #按照索引获值。
__len__ #获得长度。
__cmp__ #比较运算。
__call__ #函数调用。
__add__,__sub__,__mul__,__truediv__,__mod__,__pow__ #分别对应数学运算。
四 定义对象
在python中一切皆对象。定义的类本身也是一个以该类名称为名称的对象,称类对象。用户可以通过使用“类对象.类属性”的方式引用类属性。不能通过类对象引用其他属性和带哦用类的其他方法。
实例对象是类对象实例化的产物,定义实例对象的一般格式为:实例对象名称=类名称([参数列表])。通过实例对象可以引用类的所有非私有属性和菲斯有方法,对于实例对象而言,类属性是不存在的。
五 方法的参数传递
1)参数为不可变数据类型
在这种情况下参数的传递采用值复制的形式,即实参值直接赋值给对应的形参,再执行被调用的方法,返回时不会改变实参值。
class A:
def sum(self, n, s):
s = n*(n+1)//2 #这里的值全为形参,和c语言里的一样。
a = A()
s = 0
a.sum(5, s)
print(s)
输出s为0.
2)参数为可变数据类型的情况
当参数为可变数据类型时,形参的结果会传递给实参。(感觉有指针内味了)
比如上面那个把s换成列表就可以了。
class A:
def sum(self, n, s):
s.append(n*(n+1)//2) #可变数据类型如列表不要直接赋值。
a = A()
s = []
a.sum(5, s)
print(s[0])
python中的对象由对象名和实例构成。对象名中存放的是实例的地址,在调用的时候采用值传递,也就是实参对象名中存放的地址复制给形参,这样形参和实参指向相同的实例!(和指针差不多。。。)通过形参改变实例,那么实参指向的实例也变了,相当于将实例的改变传递回了实参。和c语言函数中可以用指针作为变量差不多属于是。
六 继承
1)单继承
基本格式如下:其实很好理解。
class 子类名称(父类名称):
语句1
...
语句n
父类和子类定义在一个作用域里面,如果父类定影在另一个模块中,则需要用“模块名称.”来引用。
如果在子类中需要父类的构造方法,则需要显式地调用父类的构造方法,或者不重写父类的构造方法。如果子类不重写__init__(),在实例化子类的时候,会自动调用父类定义的__init__()。在子类中调用父类构造方法的方法为:
super().__init__(参数1,参数2,...)
其中super()的返回值为一个特殊的对象,该对象专门用来调用父类中的属性。
子类将继承父类的所有非私有属性和非私有方法,但不继承私有的。
子类在查找属性或者方法时优先在自己本身查找,找不到才会找父类。子类可以定义自己的新属性,若跟父类重名,以子类自己的为准。方法同理。
class People: #定义父类
def __init__(self, n, a, w): #构造方法
self.name = n
self.age = a
self.__weight = w
def dispp(self):
print("My name is %s,I weigh %s kilos"% (self.name, self.__weight), end='')
class Student(People): #定义子类
def __init__(self, n, a, w, g): #子类的构造方法
super().__init__(n, a, w) #调用父类的构造方法
#这里也可以用People.__init__(self,n,a,w)
self.grade = g
def disps(self):
super().dispp() #调用父类的构造方法
print("My age is %d,and I\'m in %d grade." % (self.age, self.grade))
s = Student('John', 10, 50, 3)
s.disps()
2)多继承
多继承时直接继承多个父类。需要注意若跟父类中有相同的方法名,而在子类中未指定时,Python将按照书写顺序从左往右搜索,即方法在子类中未找到的时候就会从左往右找父类中是否包含这个方法。
class A:
def disp(self):
print("A")
class B(A):
def disp(self):
print("In B")
super().disp() #调用C的disp
print('Out B')
class C(A):
def disp(self):
print("In C")
super().disp() #调用A的disp
print('Out C')
class D(B, C):
def disp(self):
print("In D")
super().disp() #调用B的disp
print('Out D')
print(D.__mro__) #输出解析顺序
d = D()
d.disp()
解析顺序输出结果为:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
也就是说执行d.disp()的时候,限制的D类的disp()方法,由于D类的继承顺序时先B后C,所以先调用B再调用C。所以D.disp()中的super().disp()先调用B.disp(),再调用C.disp(),最后调用A.disp()。结果如下:
In D
In B
In C
A
Out C
Out B
Out D
七 异常处理
在python中最常见的异常处理方法为try语句。常见异常类型如下:
Exception #常见类型的基类。Common base class for all non-exit exceptions.
StopIteration #迭代器没有更多的值。Signal the end from iterator.__next__().
FloatingPointError #浮点计算错误。 Floating point operation failed.
ZeroDivisionError #模或除零出现错误。Second argument to a division or modulo operation was zero.
AssertionError #在assert语句失败时引发。Assertion failed.
FileExistsError #创建已存在的文件或者目录。File already exists.
FileNotFoundError #未找到相应文件或目录。File not found.
简单应用:
try:
x = 4/0
except ZeroDivisionError:
print('0不能作为除数!!')
可以使用raise抛出一个异常。
def openfile(name):
try:
f = open(name, 'r')
except:
raise Exception('')
print(name+'文件打开成功')
f.close
name = 'xyz.txt'
try:
openfile(name)
except:
print(name+'文件不存在')
执行上述程序时,若某文件存在,则提示打开成功;反之抛出一个Exception异常,被下面的except捕获到。
实际上,断言assert Exception等价于:
if not Exception:
raise AssertionError
八 迭代器和生成器
1)迭代器
在python中字符串、列表、元组、字典类型都是可以迭代的对象(iterable),简单说就是可遍历。
迭代器有两个基本函数,iter()和next()。前者建立可迭代对象的迭代器对象,后者用来返回迭代器的下一个原始,迭代结束则抛出StopIteration异常。如:
a = [1, 2, 3, 4, 5] # 定义可迭代对象a
it = iter(a) # 建立a的迭代对象it
while True:
try:
x = next(it) # 获得下一个元素值
print(x)
except StopIteration:
break
next()可以带一个参数:
next(iterator[,default])
default用于设置在没有下一个元素时返回该默认值,不设置则会触发StopIteration异常。因此上述代码和下面等价:
a = [1, 2, 3, 4, 5]
it = iter(a)
while True:
x = next(it, 'ero')
if x == 'ero':
break
print(x)
2)生成器
在python中,使用了一个或者多个yield的函数被称为生成器(generator)。生成器是一个返回迭代器的函数。每次运行到yield就会暂停并保存当前的运行信息,返回yield的值,且在下一次执行next()的时候从当前位置继续运行。
def revstr(str):
n = len(str)
for i in range(n-1, -1, -1):
yield str[i]
it = revstr('hello')
while True:
x = next(it, '#')
if x == '#':
break
print(x, end='')
上面是一个字符串倒写程序。