【Python学习】面向对象高级编程

(一)使用__slots__

当定义了一个class,创建了一个class的实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
正常情况

from types import MethodType

# 定义class
class Student():
	pass
	
def set_age(self,age):
		self.age = age
# 给实例绑定属性
s = Student()
s.name = 'luna'
print(s.name)

# 给实例绑定方法
s.set_age = MethodType(set_age,s)
# 调用实例方法
s.set_age(25)
print(s.age)

# 给class绑定方法
def set_score(self,score):
	self.score = score

Student.set_score = set_score # 调用
# Student.set_score=MethodType(set_score,Student) 给类绑定方法
s.set_score(100)
print(s.score)

通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。

【使用__slots__】
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student():
	__slots__ = ('name','age') # 用元组定义允许绑定的属性名称

s = Student() # 创建实例
s.name = 'luna' # 绑定属性
s.age = 13
s.score = 90

由于’score’没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

(二) 使用@property

Python内置的@property装饰器就是负责把一个方法变成属性调用。

class Student():

    @property # 只读属性
    def score(self):
        return self._score

    @score.setter  # 可读写属性
    def score(self,value):
        if not isinstance(value,int):
            raise ValueError('必须是整数类型')
        if value < 0 or value > 100:
            raise ValueError('必须是0-100之间')
        self._score = value

s = Student()
s.score = 60
print(s.score)

s.score = 101
print(s.score)

(三)多重继承

继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。

# 通过多重继承,一个子类就可以同时获得多个父类的所有功能
class Animal():
    pass

# 大类
class Mammal(Animal):
    pass

class Bird(Animal):
    pass

# 功能类
class Runnable():
    def run(self):
        print('running...')

class Flyable():
    def fly(self):
        print('flying...')

# 各种动物
class Dog(Mammal,Runnable):
    pass
class Parrot(Bird,Flyable):
    pass

MixIn
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系,不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

(四)定制类

__str__:返回用户看到的字符串

# __str__
class Student():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student Object(name:%s)' % self.name


print(Student('luna'))

__iter__:一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,返回一个迭代对象,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

# __str__
class Student():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student Object(name:%s)' % self.name


print(Student('luna'))


# __iter__
class Fib():
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 1000:
            raise StopIteration()
        return self.a

    def __getitem__(self, n):
        if isinstance(n, int):  # n是序列
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice):  # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L


for n in Fib():
    print(n)

f = Fib()
print(f[3])

# 切片
print(f[0:3])

如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

__getattr__:正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错
调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值。

# __getattr__
class Animal():
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):
        if item == 'score':
            return '0' # 也可以返回函数

a = Animal('koko')
print(a.name)
print(a.score)

只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

完全动态的__getattr__,写出一个链式调用:

class Chain():
    def __init__(self,path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s'%(self._path,path))

    def __str__(self):
        return self._path

    __repr__ = __str__

print(Chain().status.user.timeline.list)

__call__:任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用

class Chain():
    def __init__(self,path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s'%(self._path,path))

    def __call__(self, path):
        return  Chain('%s/%s'%(self._path,path))

    def __str__(self):
        return self._path

    __repr__ = __str__

print(Chain().status.user.timeline.list)
print(Chain().users('miko').repos)

分解 Chain().users('miko').repos

# Chain().users('michael').repos 分解
urls = Chain()    # 初始化一个实例
urls = urls.users    # 查找实例的一个属性
urls = urls('michael)    # 调用一个函数
urls = urls.repos    # 还是实例的属性

1、第一步

urls = Chain()

初始化一个实例,此时urls等于,因为定义了默认值path=''

2、第二步4

urls = urls.users

查找urls的属性users,没找到定义的属性,那就调用__getattr__方法,返回了一个函数调用:

def __getattr__(self, users):
    return Chain('%s/%s' % (self.__path, users))

这一步调用了Chain(),而且把要查找的属性users作为参数传递了进去,也就是Chain(users),那么根据Chain()的逻辑,最后返回的是:/users,然后跟上一步的结果拼接,最终返回:/users
3、第三步

urls = urls('michael')

对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。 原来是为了让实例化对象和函数一样可以被使用。
然后调用urls = urls('michael'),那么最终返回:/users/michael
4、第四步

urls = u.repos

和第二步没什么区别,所以urls最为:/users/michael/repos;

(五)使用枚举类

枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、状态等。
一般就通过字典或类来实现:

使用 Enum

# 使用枚举
from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2
    YELLOW = 3


# 枚举成员有值(默认可重复)、枚举类型不可实例化,不可更改
print(Color.GREEN)
print(repr(Color.GREEN))
print(type(Color.GREEN))
print(isinstance(Color.GREEN, Color))
Color.GREEN
<Color.GREEN: 2>
<enum 'Color'>
True、

定义枚举

1、定义枚举时,成员名不能重复
2、成员值允许相同,第二个成员的名称被视作第一个成员的别名
3、若要不能定义相同的成员值,可以通过 unique 装饰

# 定义枚举
class Code(Enum):
    GA = 1
    GB = 2
    GG = 3
    GX = 2 # 成员值允许相同
print(Code.GA.value)

@unique #能定义相同的成员值
class Code2(Enum):
    GA = 1
    GB = 2
    GG = 3
print(Code2.GB.value)

枚举取值

通过成员名来获取成员也可以通过成员值来获取成员:

# 枚举取值
# 获取成员
print(Code2['GA'])  # Code2.GA 通过成员名来获取成员
print(Code2(1))  # Code2.GA  通过成员值来获取成员
# 成员属性和值
c = Code2.GA
print(c.name)  # GA
print(c.value)  # 1
# 支持迭代的方式遍历成员,按定义的顺序,如果有值重复的成员,只获取重复的第一个成员
for cd in Code:
    print(cd)
# 特殊属性 __members__ 是一个将名称映射到成员的有序字典,也可以通过它来完成遍历
for code in Code.__members__.items():
    print(code)
('GA', <Code.GA: 1>)
('GB', <Code.GB: 2>)
('GG', <Code.GG: 3>)
('GX', <Code.GB: 2>)

枚举比较

枚举的成员可以通过 is 同一性比较或通过 == 等值比较,枚举成员不能进行大小比较:

# 枚举比较
print(Code.GA is Code.GA)
print(Code.GA is not Code.GB)
print(Code.GA ==Code.GA)
print(Code.GA != Code.GB)

扩展枚举 IntEnum

IntEnum 是 Enum 的扩展,不同类型的整数枚举也可以相互比较

from enum import IntEnum
class A(IntEnum):
    a = 1
    b = 2

class B(IntEnum):
    a = 1
    b = 1

print(A.a == 1)
print(A.a < 3)
print(A.a == b.A)
print(A.a < B.b)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值