面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。
给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。
数据封装、继承和多态是面向对象的三大特点。以上正文摘自廖雪峰Python教程之面向对象。
1、类与实例
# 类
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))
def get_grade(self):
if self.__score >= 90:
print('成绩优秀')
elif self.__score >= 60:
print('成绩合格')
else:
print('成绩不合格')
# getters
def get_name(self):
return self.__name
def get_score(self):
return self.__score
# setters
def set_name(self, name):
self.__name = name
def set_score(self, score):
self.__score = score
# 实例对象
bar = Student('Bar', 80)
foo = Student('Foo', 90)
bar.print_score() # 'Bar: 80'
foo.print_score() # 'Foo: 90'
bar.get_grade() # '成绩合格'
foo.get_grade() # '成绩优秀'
print(bar.get_name()) # 'Bar'
foo.set_score(10)
print(foo.get_score()) # 10
(1)类与实例
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响。
class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
特殊方法“init”,其第一个参数永远是self,表示创建的实例本身,因此,在init方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
(2)访问控制
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
-
_xxx
代表是局部的变量,python不建议在外部访问。 -
__xxx
代表是 private私有变量,外部不能访问。 -
__xxx__
代表特殊变量,这不是私有变量,外部可以直接访问。
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
2、继承与多态
# 基类、父类、超类
class Animal(object):
def run(self):
print('动物正在跑步。。。')
# 继承自Animal类
class Dog(Animal):
# pass
# 覆写父类方法
def run(self):
print('狗狗正在跑步。。。')
# 定义子类自有的方法
def eat(self):
print('狗狗正在吃饱。。。')
# 继承自Animal类
class Cat(Animal):
# pass
def cry(self):
print('猫猫正在喵喵。。。')
dog = Dog()
dog.run() # 狗狗正在跑步。。。
dog.eat() # 狗狗正在吃饱。。。
cat = Cat()
cat.run() # 动物正在跑步。。。
cat.cry() # 猫猫正在喵喵。。。
(1)继承
被继承的class称为基类、父类或超类(Base class、Super class)。继承有什么好处?最大的好处是子类获得了父类的全部功能。
(2)多态
继承的另一个好处,是多态。要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。此处的 Animal、Dog、Cat 和 list、str、dict 没有什么两样。
在这个例子中,dog对象可以看成是Dog类型,也可以看成是Animal类型,这便是多态。那么多态有什么好处呢?
# 演示:多态的好处
def run_twice(animal):
animal.run()
animal.run()
run_twice(Animal()) # 打印两次“动物正在跑步。。。”
run_twice(dog) # 打印两次“狗狗正在跑步。。。”
run_twice(cat) # 打印两次“动物正在跑步。。。”
class Pig(Animal):
def run(self):
print('猪猪正在跑步。。。')
def sleep(self):
print('猪猪正在睡觉。。。')
run_twice(Pig()) # 打印两次“猪猪正在跑步。。。”
多态的好处就是:当我们调用run_twice()
函数时,无论我们传入Animal、Dog、Cat、Pig等类型的实例对象时,只要这个实例对象是Animal类型(多态),那么都能够打印出run()
结果,而不用关心这个实例对象本身的类是否有定义这个run()
方法,因为Animal基类中定义了。
这符合著名的“开闭原则”:对扩展开放,允许新增Animal子类;对修改封闭,不需要修改依赖Animal类型的run_twice()等函数。
(3)鸭子类型
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系。
# 鸭子类型
# Duck继承自object,没有继承自Animal
class Duck(object):
def run(self):
print('鸭子正在跑步。。。')
run_twice(Duck()) # 也会打印两次“鸭子正在跑步。。。”
3、实例属性和类属性
class Timer(object):
name = 'timer' # 类属性
t = Timer()
print(t.name) # timer
print(Timer.name) # timer
t.name = 'timer-2'
print(t.name) # timer-2
print(Timer.name)
del t.name
# 删除实例的t.name属性后,
# 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了。
print(t.name) # timer
print(Timer.name) # timer
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
4、获取对象信息
(1)使用 type()
print(type(123)) # <class 'int'>
print(type('hello')) # <class 'str'>
print(type(None)) # <class 'NoneType'>
print(type(abs)) # <class 'builtin_function_or_method'>
# 自定义类型
class Fruits(object):
pass
print(type(Fruits())) # <class '__main__.Fruits'>
# 在表达式中使用type()
print(type(123) == type(456)) # True
print(type(123) == int) # True
print(type('hello') == type('hi')) # True
print(type('hello') == str) # True
import types
# 自定义函数
def fn():
pass
print(type(fn) == types.FunctionType) # True
# 内置函数
print(type(abs) == types.BuiltinFunctionType) # True
# lambda匿名函数
print(type(lambda x:x) == types.LambdaType) # True
# 生成器
print(type((x for x in range(10))) == types.GeneratorType) # True
(2)使用 isinstance()
对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。isinstance()就可以告诉我们,一个对象是否是某种类型。
class Animal(object):
pass
class Dog(Animal):
pass
d = Dog()
print(isinstance(d, Dog)) # True
print(isinstance(d, Animal)) # True
能用type()判断的基本类型也可以用isinstance()判断。
print(isinstance('hello', str)) # True
print(isinstance(10, int)) # True
print(isinstance(b'hello', bytes)) # True
isinstance() 还可以判断一个变量是否是某些类型中的一种。
print(isinstance([], (list, tuple))) # True
print(isinstance((), (list, tuple))) # True
(3)使用 dir()
要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list。
print(dir('hello'))
# ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
参考资源:
1、廖雪峰Python教程
2、Python官方文档
END!!!
网友评论