面向对象编程 --Object Oriented Programming, 简称 OOP, 是一种程序设计思想. OOP 把对象作为程序的基本单元, 一个对象包含了数据和操作数据的函数.
类和实例
定义
- class Student(object):
- pass
- stone = Student()
- stone.name = "stone"
- stone.age = 2
- print(stone.name, stone.age)
上面代码中:
class 后面紧接的是类名, 类名以大写字母开头
(object)是该类从哪个类继承下来的
使用时候可以自由的给实例变量绑定属性
方法
- class Student(object):
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def print_info(self):
- print(self.name, self.age)
- stone = Student("stone", 18)
- stone.print_info()
上面代码中:
__init__方法的第一个参数永远是 self, 表示创建的实例本身, 因此, 在__init__方法内部, 就可以把各种属性绑定到 self, 因为 self 就指向创建的实例本身. 类似于 java 的构造函数.
定义一个方法, 除了第一个参数是 self 外, 其他和普通函数一样. 要调用一个方法, 只需要在实例变量上直接调用, 除了 self 不用传递, 其他参数正常传入.
访问限制
实例的变量名如果以__开头, 就变成了一个私有变量(private), 只有内部可以访问, 外部不能访问.
变量名类似__xxx__的, 也就是以双下划线开头, 并且以双下划线结尾的, 是特殊变量, 特殊变量是可以直接访问的, 不是 private 变量.
继承和多态
在 OOP 程序设计中, 当我们定义一个 class 的时候, 可以从某个现有的 class 继承, 新的 class 称为子类(Subclass), 而被继承的 class 称为基类, 父类或超类(Base class,Super class). 和 java 一样.
静态语言 vs 动态语言:
对于静态语言 (例如 Java) 来说, 如果需要传入 Animal 类型, 则传入的对象必须是 Animal 类型或者它的子类, 否则, 将无法调用 Animal 中的方法. 对于 Python 这样的动态语言来说, 则不一定需要传入 Animal 类型. 我们只需要保证传入的对象有 Animal 中的方法就可以了.
对象信息
使用 type()
- >>> 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
以上代码可以看出, 判断基本数据类型可以直接写 int,str 等, 但如果要判断一个对象是否是函数怎么办? 可以使用 types 模块中定义的常量.
使用 isinstance()
- >>> isinstance([1, 2, 3], (list, tuple))
- True
- >>> isinstance((1, 2, 3), (list, tuple))
- Tru
可以判断一个变量是否是某些类型中的一种, 比如上面的代码就可以判断是否是 list 或者 tuple
使用 dir()
如果要获得一个对象的所有属性和方法, 可以使用 dir()函数, 它返回一个包含字符串的 list, 比如, 获得一个 str 对象的所有属性和方法:
- >>> dir('ABC')
- ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
类似__xxx__的属性和方法在 Python 中都是有特殊用途的, 比如__len__方法返回长度. 在 Python 中, 如果你调用 len()函数试图获取一个对象的长度, 实际上, 在 len()函数内部, 它自动去调用该对象的__len__()方法, 所以, 下面的代码是等价的:
- >>> len('ABC')
- 3
- >>> 'ABC'.__len__()
- 3
我们自己写的类, 如果也想用 len(myObj)的话, 就自己写一个__len__()方法:
- >>> class MyDog(object):
- ... def __len__(self):
- ... return 100
- ...
- >>> dog = MyDog()
- >>> len(dog)
- 100
仅仅把属性和方法列出来是不够的, 配合 getattr(),setattr()以及 hasattr(), 我们可以直接操作一个对象的状态:
- >>> 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
也可以获得对象的方法:
- >>> hasattr(obj, 'power') # 有属性'power'吗?
- True
>>> getattr(obj, 'power') # 获取属性'power'
- <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
- >>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量 fn
>>> fn # fn 指向 obj.power
- <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
- >>> fn() # 调用 fn()与调用 obj.power()是一样的
- 81
实例属性和类属性
类本身需要绑定一个属性:
- class Student(object):
- name = 'Student'
当我们定义了一个类属性后, 这个属性虽然归类所有, 但类的所有实例都可以访问到. 来测试一下:
- >>> class Student(object):
- ... name = 'Student'
- ...
- >>> s = Student() # 创建实例 s
- >>> print(s.name) # 打印 name 属性, 因为实例并没有 name 属性, 所以会继续查找 class 的 name 属性
- Student
- >>> print(Student.name) # 打印类的 name 属性
- Student
- >>> s.name = 'Michael' # 给实例绑定 name 属性
- >>> print(s.name) # 由于实例属性优先级比类属性高, 因此, 它会屏蔽掉类的 name 属性
- Michael
- >>> print(Student.name) # 但是类属性并未消失, 用 Student.name 仍然可以访问
- Student
- >>> del s.name # 如果删除实例的 name 属性
- >>> print(s.name) # 再次调用 s.name, 由于实例的 name 属性没有找到, 类的 name 属性就显示出来了
- Student
来源: https://juejin.im/post/5b267547518825749864a1b7