返回介绍

面向对象编程

发布于 2024-05-30 23:22:17 字数 15998 浏览 0 评论 0 收藏 0

动态语言的“鸭子类型”

对于静态语言(例如 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文