面向对象编程(Object Oriented Programming),以对象作为程序的基本单元,其中对象包含了数据和操作数据的函数。
面向过程设计则将程序视作一系列命令集合,即通过函数来顺序运行,利用函数切分来降低复杂度。
面向对象设计则将程序视作一组对象的集合。对象可以接受、发送和处理信息。程序执行就是指消息在各对象的传递。
数据封装、继承和多态是面向对象的三大特点,后面会详细介绍。
Python中的所有数据类型都可以视为对象,而 类(class) 就是自定义的对象数据类型。根据类来创建对象,就是 实例化(Instance)。
class<类名>(父类名):
def __init__(self, 参数, …): # 构造函数
...
def 方法名1(self, 参数, …): # 方法1
...
def 方法名2(self, 参数, …): # 方法2
...
其中类名推荐首字母大写。
class Calculator:
name='Good Calculator' #class的属性
price=18
def add(self,x,y):
print(self.name)
result = x + y
print(result)
def minus(self,x,y):
result=x-y
print(result)
def times(self,x,y):
print(x*y)
def divide(self,x,y):
print(x/y)
类定义函数时,与普通函数定义不同的是,参数第一个必须是self
。
实例化方式:实例名=类名()
>>> cal=Calculator() #运行class的时候要加"()"
>>> cal.name
'Good Calculator'
>>> cal.price
18
>>> cal.add(10,20)
Good Calculator
30
>>> cal.minus(10,20)
-10
>>> cal.times(10,20)
200
>>> cal.divide(10,20)
0.5
数据含有自身的属性。Python中类的属性分为两类:实例属性和类属性。
定义格式:self.属性名
或实例名.属性名
class Student(object):
def __init__(self, name):
self.name = name
属性名是name
调用格式:实例名.属性名
>>> s = Student('Bob')
>>> s.name
'Bob'
定义格式:属性名=
(class中直接赋值)
class Student(object):
name = 'Bob'
调用格式:类名.类变量名
>>> print(Student.name)
Bob
class Calculator:
name='good calculator'
price=18
def __init__(self,name,price,height=10,width=14,weight=16): #后面三个属性设置默认值,查看运行
self.name=name
self.price=price
self.h=height
self.wi=width
self.we=weight
实例名.方法名
赋值修改。>>> c=Calculator('bad calculator',18)
>>> c.h
10
>>> c.wi
14
>>> c.we
16
>>> c.we=17
>>> c.we
17
class Car:
def __init__(self,make,model,year):
self.make = make
self.model = model
self.year = year
self.odometer = 0
def get_name(self):
name = str(self.year) + ' ' + self.make + ' ' + self.model
return name.title()
def read_odometer(self):
print("This car has " + str(self.odometer) + " miles on it.")
def update_odometer(self,mileage):
self.update_odometer = mileage
def update_odometer(self,mileage):
self.odometer = mileage
进行实例化
my_car = Car('audi','a4',2016)
print(my_car.get_name())
my_car.read_odometer()
输出
2016 Audi A4
This car has 0 miles on it.
更新里程表
my_car.odometer = 23
my_car.read_odometer()
输出
This car has 23 miles on it.
实例属性和类属性不要使用相同的名字,否则实例属性会屏蔽类属性
类中的函数称为方法(method),与普通的函数不同的是调用的方式。
self
实例名.方法名
的形式调用,来访问实例变量的属性。__init__() 方法用于实例化时相关数据的初始化(initial),也被称为构造函数(constructor),在生成类的实例时被调用一次。一般在创建实例的时候,将一些必须绑定的属性强制捆绑,可以使用构造函数。
注意前后分别有两个下划线,为特殊变量。
class Calculator:
name='good calculator'
price=18
def __init__(self,name,price,height,width,weight):
self.name=name
self.price=price
self.h=height
self.wi=width
self.we=weight
>>> c=Calculator('bad calculator',18,17,16,15)
实例化需要给定__init__() 中包含的所有参数的值
>>> c.name
'bad calculator'
>>> c.price
18
>>> c.h
17
>>> c.wi
16
>>> c.we
15
Python私有变量(private)以两个下划线 __开头,只有内部可以访问。但是不能以两个下划线__结尾,否则是特殊变量。
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
此时,外部无法访问实例变量.__name
和实例变量.__score
。
如果外部代码想要获取内部数据,可以在类中增加获取数据和修改数据的方法。
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_score(self, score):
self.__score = score
比直接通过实例名.属性名
进行数据修改更好的原因是,方法中可以对参数进行检查,避免传入无效的参数。
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
类的方法分为:实例方法、类方法、静态方法。
前文提到的都是实例方法,调用参数为实例。
类方法的调用参数是类。静态方法没有调用参数。两者调用前可以不实例化类。
@classmethod
进行修饰。其中类方法定义需要带默认参数。@staticmethod
进行修饰定义
class Method:
@staticmethod
def static_method():
print('调用静态方法')
@classmethod
def class_method(c):
print('调用类方法')
调用
Method.static_method()
调用静态方法
>>> Method.class_method()
调用类方法
面向对象编程的其中一个重要特点就是数据封装。
封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
优点:隐藏细节
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
实例化
>>> bob = Student()
捆绑数据属性
>>> bob.name
'Bob Smith'
>>> bart.score
80
def print_score(std):
print('%s: %s' % (std.name, std.score))
print_score(bob)
Bob Smith: 80
Student
的实例本身含有以上数据,可以直接在Student
类的内部定义访问数据的函数,而不需要在外部定义新函数进行访问,这就是数据封装。封装数据的函数和类本身是关联的,也就是前文提到的类的方法。一个类继承另一个类时,将获得另一个类所有属性和方法。
原有的类称为父类、基类或超类,新类称为子类或派生类。
另外子类可以定义自己的属性和方法,也可以修改父类的方法,但是不能继承父类的私有属性和私有方法。
所有类都可以追溯到根类object。
优点:代码重用
使用方式 class 子类名(父类名)
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass #表示空语句
class Cat(Animal):
pass
此时方法run()
得到继承
dog = Dog()
dog.run()
cat = Cat()
cat.run()
Animal is running...
Animal is running...
class Dog(Animal):
def stop(self):
print('Dog stops running.')
>>> dog = Dog()
>>> dog.run()
Animal is running...
>>> dog.stop()
Dog stops running.
父类的方法不符合子类的行为, 都可对其进行重写。可在子类中定义与要重写的父类方法同名的方法。 这样, Python将不会考虑这个父类方法, 而只关注你在子类中定义的相应方法,也称作方法重载。
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
Dog is running...
Cat is running...
建议将类细节较多时,将大型类拆分成协同工作的小类
一个类可以继承多个父类,叫多重继承。
继承方式以“,” 隔开多个父类。
格式:class 子类名(父类1,父类2,...,父类n)
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是允许将父类的引用指向子类的对象。保证子类的正确调用。
优点:接口重用
对扩展开放(Open for extension):允许子类重写方法函数
对修改封闭(Closed for modification):不重写,直接继承父类方法函数
判断对象类型,可使用type()
函数。
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
type()
函数返回对应的Class类型。可用于比较两个变量的基本类型是否相同。>>> type(123)==type(456)
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False
types
模块中定义的常量。【模块部分后面详述】>>> import types
>>> def fn():
... pass
...
>>> 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
由于类存在继承关系,使用type()
会不方便,因此判断类的类型,可以使用isinstance()
函数。【推荐!】
以上面的例子,继承关系为
object -> Animal -> Dog -> Husky
接上文,创建对象
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
用isinstance()
判断
>>> isinstance(h, Husky)
True
>>> isinstance(h, Dog)
True
>>> isinstance(d, Husky)
False
可见,h
属于Husky类本书,也属于父类Dog
。
因此,isinstance()
判断一个对象是否为该类型本身,或者位于该类型的父继承链上。
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
>>> isinstance([1, 2, 3], (list, tuple))
True
dir()
函数可获得一个对象的所有属性和方法。
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
__xxx__
为特殊变量。如函数len()
可获取对象的长度,其内部自动调用 __len__()
方法
>>> len('ABC')
3
>>> 'ABC'.__len__()
3
getattr()
、setattr()
以及hasattr()
可以直接操作对象的状态。
getattr()
用于获取属性,getattr(实例名, '属性名', <默认值>)
,可传入default参数。
setattr()
用于设置属性,setattr(实例名, '属性名', 属性值)
。
hasattr()
用于判断对象是否包含某种属性,hasattr(实例名, '属性名')
,输出True或False。
定义对象
>>> class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
>>> obj = MyObject()
测试属性
>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
getattr()
可传入default参数。
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404
getattr()
可获得对象同时赋值到变量
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn() # 调用fn()与调用obj.power()是一样的
81
但是只有不知道对象信息时,才获取信息。可以直接实例化就不要用getattr()函数。
hasattr()判断函数对于代码执行有重要作用。
====================================================================
Python学习的内容参考
《Python编程:从入门到实践》-[美] Eric Matthes
《21天学通PYTHON》
莫烦Python
廖雪峰的Python教程
等