面向对象编程
动态语言的“鸭子类型”
对于静态语言(例如 Java)来说,如果需要传入 Animal 类型,则传入的对象必须是 Animal 类型或者它的子类,否则,将无法调用 run()方法。
对于 Python 这样的动态语言来说,则不一定需要传入 Animal 类型。我们只需要保证传入的对象有一个 run()方法就可以了: 这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
获取对象信息
1)获取基本类型,使用type()
函数:
>>> type(123)
<class 'int'>
>>> type(123)==type(456)
True
>>> type(123)==int
True
2)对于 class 的继承关系来说,使用 type()
就很不方便。判断 class的类型,可以使用 isinstance()
函数:isinstance()
判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
如果继承关系是:
object -> Animal -> Dog -> Husky
那么:
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h, Husky)
True
>>> isinstance(h, Dog)
True
>>> isinstance(h, Animal)
True
>>> isinstance(d, Husky)
False
能用 type()
判断的基本类型也可以用 isinstance()
判断:
>>> isinstance('a', str)
True
并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是 list 或者 tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
3)获得一个对象的所有属性和方法,可以使用 dir()
函数,它返回一个包含字符串的 list,比如,获得一个 str 对象的所有属性和方法:
>>> dir('ABC')
4)使用 hasattr()
、getattr()
和setattr()
,来测试属性是否存在、获取一个属性和设置一个属性。
定义一个了如下的类:
class My:
def __init__(self):
self.x = 9
def show(self):
print(x)
>>> my = My()
>>> hasattr(my, 'x') # 有属性'x'吗?
True
>>> hasattr(my, 'show') # 有属性'show'吗?
True
类的定义
类变量、类方法(静态方法): 类名.xxx
or 实例名.xxx
实例变量、实例方法: 实例名.xxx
class School:
population = 0 #类变量
def __init__(self, name):
School.population += 1
self.name = name #实例变量
def __del__(self):
School.population -= 1
def sayhi(self): #实例方法
print('hello {}'.format(self.name))
@classmethod
def howmany(cls): #类方法:有一个自动赋值为类名的形参,即cls会被自动赋值为类名School
print(cls.population)
@staticmethod
def howmany2(): #静态方法:不需要一个自动赋值的形参
print(School.population)
s = School('erick')
print(s.name)
s.sayhi()
print(School.population)
School.howmany()
del s
School.howmany2()
构造函数
如果我们没有在一个子类中定义__init__
方法,Python 将会自动调用基类的构造函数。
如果我们在一个子类中定义了__init__
方法,Python 不会自动调用基类的构造函数,你必须自己显式地调用它。
class A:
def __init__(self):
print('A init')
class B(A):
def __init__(self):
A.__init__(self) #显式地调用基类的构造函数
print('B init')
b = B()
注:在子类中调用基类的方法格式为“
基类名.方法名
”。
类属性和实例属性
类属性:直接在 class 中定的义属性,这个属性归类所有,并且类的所有实例都可以访问到。
实例属性:通过实例变量,或者通过 self 变量绑定的属性。
例如:
class Student:
school = 'nchu' #类属性
def __init__(self, name):
self.name = name #通过self变量绑定的属性
s = Student('Jack')
print(Student.school) #访问类的属性,输出:nchu
print(s.school) #输出:nchu
print(s.name) #输出:Jack
s.school = 'tinghua' #通过实例变量绑定一个实例属性school,在这里它和类属性同名
print(Student.school) #输出:nchu
print(s.school) #输出:tinghua
del s.school #删除实例属性
print(s.school) #输出:nchu
通过"类名.属性"访问类属性;
通过"实例变量.属性"访问实例属性,如果没找到,则继续访问类属性;如果实例属性和类属性同名,则实例属性将屏蔽掉相同名称的类属性,优先使用实例属性。
动态绑定
Python 允许动态的对实例变量绑定属性和方法,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
尝试给实例绑定一个属性:
class Student(object):
pass
s = Student()
s.name = 'Michael' # 动态给实例绑定一个属性
还可以尝试给实例绑定一个方法:
from types import MethodType
def set_age(self, age):
self.age = age
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
s.set_age(25) # 调用实例方法
为了给所有实例都绑定方法,可以给 class 绑定方法:
Student.set_age = MethodType(set_age, Student)
s2 = Student()
s2.set_age(30)
使用slots
有时候需要限制实例属性的动态绑定,为了达到限制的目的, Python 允许在定义 class 的时候,定义一个特殊的__slots__
变量,来限制该 class 实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age')
# 用 tuple 定义所有允许绑定的属性名称,这里表示只允许添加name和age两个属性
需要注意, __slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的;除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
使用@property
Python 内置的@property 装饰器是负责把一个方法变成属性调用的。@property 广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
把一个 getter 方法变成属性,只需要加上@property
就可以了;
在这个基础上还可以使用另一个装饰器@score.setter
把一个 setter 方法变成属性。
只定义 getter 方法,不定义 setter 方法就是一个只读属性。
class Stu:
@property #getter方法
def val(self):
return self.__val
@val.setter #setter方法
def val(self, value):
if not isinstance(value, int):
raise ValueError('val must be an integer.')
if value < 0 or value > 100:
raise ValueError('val is 0~100')
self.__val = value
@property #getter方法
def age(self):
return 100
s = Stu()
s.val = 100 #实际转化为s.set_val(100)
print(s.val) #实际转化为s.get_val()
print(s.age) #实际转化为s.get_age()
多重继承
通过多重继承,一个子类就可以同时获得多个父类的所有功能。
class Child(Father1, Father2, Father3):
pass
在设计类的继承关系时,通常,主线都是单一继承下来的.如果需要“混入”额外的功能,通过多重继承就可以实现。这种设计通常称之为 MixIn。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论