如果有其他语言(比如java)的面向对象编程经验,这一部分就很容易理解了。
面向对象编程三大特点:封装、继承、多态。
封装的直接体现就是类class,隐藏对象的属性和实现细节,对外仅提供接口。
继承就是子类继承父类,同时拥有了父类所有属性。
多态就是方法参数为父类,调用该方法时,可以传递子类和父类对象实例,然后在代码执行时,动态执行子类 or 父类中的方法。
object是所有类的父类,当父类是object的时候,可省略。下面定义一个Person类,object表示被继承的类,__init__是类不能少的初始化的方法。
class Person(object):
# 第一个参数永远都是self,指向创建的实例本身,创建实例的时候,self参数不需要传递,其他参数必须匹配。
# 类内部的变量一般私有化,避免外部直接访问,可以提供get set方法让外部调用。
# 变量前加_,就变成私有变量,但外部还是可以通过实例._name引用。按照规定,别这么干。
# 变量前加__(双下划线),就变成私有变量,外部无法通过实例.__name获取值。
# python解释器会把__name变更为_Person__name,不同解释器变更后的变量名可能不同,所以别这么干。
# 外部通过实例.__name = 'xx'赋值后,并没有更改内部__name的值,因为内部的变量名已经变更了,可以通过get_name方法检验。
# 变量名类似__xxx__的是特殊变量,可以直接引用
def __init__(self, name, age):
self.__name = name
self._age = age
def do(self):
print('%s is %d years old.' % (self.__name, self._age))
def get_name(self):
return self.__name
def set_name(self, name):
if isinstance(name, str):
self.__name = name
else:
raise ValueError('bad name')
p = Person('tom', 18)
p.do() --> 结果: tom is 18 years old.
print(p._Person__name) --> 结果: tom
p.__name = 'new'
print(p.__name) --> 结果: new
print(p.get_name()) --> 结果: tom
# 定义子类,继承Person类
class Student(Person):
def do(self):
print('Student do run...')
s = Student('lily', 20)
s.do() --> 结果:Student do run...
print(isinstance(s, Student)) --> 结果:True
print(isinstance(s, Person)) --> 结果:True
python是动态语言,有“鸭子类型”(外表像鸭子,走起来像鸭子),不需要严格的继承体系,对象只需要看起来像即可。
比如参数person对象不一定要继承Person,只需要该对象有对应的方法即可。
# 多态示例
def duo_tai(person):
person.do()
duo_tai(Person('lilei', 20)) --> 结果: lilei is 18 years old. 动态执行Person中的do方法
duo_tai(Student('lilei', 20)) --> 结果:Student do run...动态执行Student中的do方法
# 鸭子类型示例,不继承Person,但是也有do方法,所以可以调用duo_tai方法
class Other(object):
def do(self):
print('other is run...')
duo_tai(Other()) --> 结果:other is run...
type(obj)可以获取obj的类型,但是都可以用isinstance(obj, type)代替。
import types
print(type(123)) --> 结果:int
# 判断函数类型
print(isinstance(duo_tai, types.FunctionType))
print(isinstance(abs, types.BuiltinFunctionType))
print(isinstance(lambda x: x, types.LambdaType))
print(isinstance((x for x in range(10)), types.GeneratorType))
# 还可以检测对象类型是type中的哪一个
print(isinstance([1, 2, 3], (list, tuple, int)))
类似__xxx__的属性和方法都是有特殊用途的,比如执行len()函数,都会自动去调用对象内部的len()方法
print(len('ABC')) #其实调用的是'ABC'.__len__()方法
print('ABC'.__len__())
反射特性
# 获取变量
print(getattr(p, '_sex', 'female'))
if hasattr(p, '_sex'):
print('_sex', p._sex)
else:
setattr(p, '_sex', 'male')
print('_sex', p._sex)
# 获取方法
func = getattr(p, 'do')
func()
类属性和实例属性
class Teacher:
name = 'FUCK'
t = Teacher()
print(Teacher.name) --> 结果:'FUCK'
# 实例属性未定义,将去寻找类属性,所以要避免实例属性和类属性重名
print(t.name) --> 结果:'FUCK'
t.name = 'FUCK TOO'
# 变更实例属性,类属性不变
print(Teacher.name) --> 结果:'FUCK'
# 实例属性已定义,不去寻找类属性,即实例属性优先级高于类属性
print(t.name) --> 结果:'FUCK TOO'