面向对象编程
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
(一)类和实例
面向对象最重要的概念就是类(Class)和实例(Instance),class后面接着类名,类名首字母大写,所有类都会继承object类。
# 定义类对象
class Student():
'初始化属性'
def __init__(self, name, score):
self.name = name
self.score = score
# 数据封装(数据和逻辑被“封装”起) 类方法
def print_score(self):
print('%s:%s' % (self.name, self.score))
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
# 创建实例
a_stu = Student('luna', 98);
a_stu.print_score()
print(a_stu.get_grade())
__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
(二)访问限制
要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
# 定义对象
class Student():
# 初始化属性
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
# 获取属性
def get_name(self):
return self.__name
def get_score(self):
return self.__score
# 修改属性
def set_score(self):
self.__score = score
外部无法访问 实例.__name和实例.__score了。外部如果想获取name和score,可增加get_name()方法。
变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
(三)继承和多态
定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类。
# 父类
class Animal():
def run(self):
print('动物正在跑。。。')
# 子类(继承父类)
class Dog(Animal):
# 覆盖父类方法 重写父类方法 (多态)
def run(self):
print('狗正在跑。。。')
def eat(self):
print('狗正在吃。。。')
class Cat(Animal):
def run(self):
print('猫正在跑。。。')
def eat(self):
print('猫正在吃。。。')
# 实例化
dog = Dog()
dog.run()
cat = Cat()
cat.run()
cat.eat()
子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,自动拥有了run()方法。当子类和父类都存在相同的run()方法时,子类的run()覆盖(重写)了父类的run(),在代码运行的时候,总是会调用子类的run()。这就是继承好处之一:多态。
# dog不仅仅是Dog,还是Animal
print(isinstance(dog,Dog)) # True
print(isinstance(dog,Animal)) # True
- 多态:a,b,c类里都有run这个鸭子方法,a,b,c无论是什么类型都是鸭子,可以运行
- 继承:a自身有run方法,d,c会从继承父类里调用run方法,可以运行
- 只要在父类里定义了鸭子方法,鸭子是父类和它的子类至于实际调用时子类是用自身的方法还是父类的方法根据子类自己决定
# 父类
class Animal():
def run(self):
print('动物正在跑。。。')
# 子类(继承父类)
class Dog(Animal):
# 覆盖父类方法 重写父类方法 (多态)
def run(self):
print('狗正在跑。。。')
def eat(self):
print('狗正在吃。。。')
class Cat(Animal):
def run(self):
print('猫正在跑。。。')
def eat(self):
print('猫正在吃。。。')
def Run_twice(animal):
animal.run()
animal.run()
# 实例化
a = Dog()
b = Cat()
c = Animal()
Run_twice(a)
Run_twice(b)
Run_twice(c)
[鸭子类型]:
把类Animal当做鸭子,把调用run函数当做鸭子的走路姿势,只要这个类可以调用run函数,就把它看做鸭子也就是说走路姿势像鸭子,你就是鸭子。
(四)获取对象信息
当拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?
1、使用type()
判断对象类型使用type()函数,基本类型都可以用type()判断,使用type()函数,可以获取任何数据的类型。如果要判断一个数据是不是函数,可以使用types模块中定义的常量,如:types.FunctionType、types.LambdaType。
# type判断对象
print(type(111))
print(type('123'))
print(type(None))
# 结果
<class 'int'>
<class 'str'>
<class 'NoneType'>
如果一个变量指向函数或者类,也可以用type()判断
判断一个对象是否是函数:
import types
def fn():
pass
print(type(fn) == types.FunctionType)
print(type(abs) == types.BuiltinFunctionType)
print(type(lambda x: x) == types.LambdaType)
print(type((x for x in range(10))) == types.GeneratorType)
2、使用isinstance()
对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数,isinstance函数判断的是一个对象是否是该类型或位于该类型的父类继承链上。isinstance还可以判断一个变量是否是某些类型的一种,用元组写多种类型。
继承关系:object -> Animal -> Dog -> Husky
# isinstance()
class Animal():
pass
class Dog(Animal):
pass
class Husky(Dog):
pass
a = Animal()
b = Dog()
c = Husky()
print(isinstance(c, Husky)) # True
print(isinstance(c, Dog)) # True
print(isinstance(c, Animal)) # True
能用type()判断的基本类型也可以用isinstance()判断,优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。
3、使用dir()
1.如果要获得一个对象全部的属性和方法,可以使用dir()函数。它返回一个包含 字符串的list。
2.类似“xxx”的属性和方法在Python中都是有特殊用途的,比如len()函数获取对象的长度。但实际上,在len函数内部它会去自动调用对象的__len__()方法,所以,你可以在类中自己设定一个__len__()方法,让len返回你想要返回的长度。
4、操作一个对象状态
getattr() 获取、setattr() 设置 和hasattr() 有没有 方法,可以直接操作一个对象的状态。
hasattr(obj,'x') #有属性‘x’吗?
setattr(obj,'y',18) #设置一个属性‘y’,值为18.
getattr(obj,'y') #获取属性y
getattr(obj,'z',404) #获取属性z,如果不存在,就返回默认值404.
(五)实例属性和类属性
Python是动态语言,根据类创建的实例可以任意绑定属性,给实例绑定属性的方法是通过实例变量,或者通过self变量:
编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
class Student():
def __init__(self):
self.name = name
s = Student('Tom')
s.score = 90
- 实例属性属于各个实例所有,互不干扰;
- 类属性属于类所有,所有实例共享一个属性;
- 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。