python面向对象的进阶(slots&property&多重继承&定制类&枚举类)(6)


前言

python学习笔记 (仅供学习使用)

在Python中,面向对象还有很多高级特性,允许我们写出非常强大的功能。
python是动态语⾔,动态编程语⾔ 是 ⾼级程序设计语⾔ 的⼀个类别,在计算机科学领域已被⼴泛应⽤。它是⼀类在 运⾏时可以改变其结构 的语⾔ :例如新的函数、对象、甚⾄代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语⾔⽬前⾮常具有活⼒


一、动态给类和对象赋予属性和函数

import types
class Person(object):

    def __init__(self,x_name,x_age):
        self.name = x_name
        self.age = x_age

if __name__ == '__main__':
    p = Person('zhangsan',23)
    # 1、 可不可以动态的给对象p赋予一个新的对象属性
    Person.address="北京"
    p.sex ='男'
    print(p.sex)

    # 2、 可以给当前对象p 动态的赋予一个新的对象函数吗
    def run(self,work):
        print('%s正在完成的工作是:%s'%(self.name,work))

    p.run =types.MethodType(run,p) # 给对象p添加一个实例(成员,对象)函数
    p.run('学习')

    # 3、给Person类动态赋予一个类函数
    @classmethod
    def class_run(cls,work):
        print("类函数中的work是:%s"%work)

    Person.work = class_run
    p.work('学习python')

    # 4、 给Person类动态赋予一个静态函数
    @staticmethod
    def static_run(work):
        print('静态函数中的work是:%s'%work)

    Person.staticRun = static_run
    p.staticRun('学习全栈')
    Person.staticRun('学习全栈')

那既然有增加,就有删除
删除对象与属性的方法

  1. del 对象.属性名
  2. delattr(对象, “属性名”)
    我们知道,正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:然后尝试给实例绑定一个属性,还可以绑定一个方法,但是一个实例方法对另一个实例不起作用,那就得给类整个类绑定一个方法或属性,这样所有的实例都可以调用
    需要注意的是我们的动态语言在运行后还能修改的,但是静态语言是不可以的,这就会造成不严谨

二、slots(限制属性的增加)

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

动态语⾔:可以在运⾏的过程中,修改代码(python)

静态语⾔:编译时已经确定好代码,运⾏过程中不能修改
但是,如果我们想要限制class的属性怎么办?比如,上例只允许对人类的实例添加 name 和 age 属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的 slots 变量,来限制该class能添加的属性:

#限制类动态增加属性
class Student(object):
    #只允许当前Student拥有name和sex属性
    __slots__ = ('name','sex')
#验证slots
    s = Student()
    s.name='lisi'
    s.sex= '女'
    # Student.age =10 类属性没有限制 ,可以动态添加
    s.age =23
    print(s.age)

run:

Traceback (most recent call last):
  File "/Person.py", line 54, in <module>
    s.age =23
AttributeError: 'Student' object has no attribute 'age'

三、@property装饰器

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改
@property类似于java中的getset方法。


class Person(object):
    # age 属性的值限制的范围是0——88
    def get_age(self):
        return self._age

    def set_age(self,value):
        if value>=0 and value <=88:
            self._age=value
        else:
            # print("age的值必须在0到88之间")
            self._age=0 # 0为age的初始值
            raise ValueError('age的值必须在0到88之间')#raise是抛出异常。


class Person_Property(object):

    # age 属性的值限制的范围是0——88
    @property  #age 属性暴露出去
    def age(self):
        return self._age

    @age.setter # 当前age属性可以允许赋值
    def age(self,value):
        if value>=0 and value <=88:
            self._age=value
        else:
            # print("age的值必须在0到88之间")
            self._age=0 # 0为age的初始值
            raise ValueError('age的值必须在0到88之间')

    @property
    def name(self):
        self._name = "张三"#此处必须使用_name,表私有属性
        return self._name


if __name__ == '__main__':
    p = Person_Property()
    p.age = 18
    print(p.age) # age属性可以读,可以写
    # p.name = "Lisi"
    print(p.name) # name属性是只读的

四、多重继承

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

class Father(object):
    def work(self):
        print("父亲在工作")

class Mather(object):
    def work(self):
        print("母亲在工作")

class Child(Mather,Father): # 多继承
    def __init__(self,xname):
        self.name = xname

    def work(self):
        print("自己在工作")


if __name__ == '__main__':
    c = Child('张三')
    c.work() # 如果多个父类中都有同样的函数,按照优先级来调用
    print(Child.__mro__) # 打印Child的继承结构,并且按照优先级

run:

自己在工作
(<class '__main__.Child'>, <class '__main__.Mather'>, <class '__main__.Father'>, <class 'object'>)

五、定制类

看到类似 slots 这种形如 xxx 的变量或者函数名就要注意,这些在Python中是有特殊用途的。
slots 我们已经知道怎么用了, len() 方法我们也知道是为了能让class作用于 len() 函数。这些在Python有另外的一些名称叫魔术方法
除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。


class Person(object):

    def __init__(self,xname):#重写初始化函数
        self.name = xname
        # 斐波拉契数列前两个值是固定
        self.a ,self.b = 0,1

    # 用于定制对象的描述信息
    def __str__(self):
        return "Person object (name :%s)"%self.name

    # person默认不是可迭代对象,变成一个可迭代对象,必须返回一个迭代器
    def __iter__(self):  # 生成一个斐波拉契数列
        return self

    # person就变成一个迭代器
    def __next__(self):
        self.a ,self.b = self.b, self.a+self.b # 计算下一个值
        if self.a > 1000: # 如果出现一个大于1000的数字,退出循环
            raise StopIteration
        return  self.a

    # def __getitem__(self, item):
    #     a, b = 1, 1
    #     for x in range(item):
    #         a, b = b, a + b
    #     return a

# person就可以类似于list
    def __getitem__(self, item): # item 可能是一个下标,还有可能是一个切片(范围)
        if isinstance(item,int): # item是一个下标,如果是整数
            a,b =1,1
            for x in range(item):
                a ,b = b,a+b
            return a
        elif isinstance(item,slice): # item是一个范围,如果是范围
            start = item.start
            stop = item.stop
            if start is None:
                start = 0 # start默认值
            a,b =1,1
            L = []
            for x in range(stop):
                if x>=start:
                    L.append(a)
                a,b =b,a+b
            return L

    # 当访问person对象中不存在的属性和函数的时候会抛出AttributeError,如果不想看到这个error则需要重写
    def __getattr__(self, item):
        if item =='age':
            return 18
        elif item == 'eat':
            return lambda : print('eat函数执行')

    def __call__(self, *args, **kwargs):
        print('person 对象可以调用')

if __name__ == '__main__':
    p = Person('张三')
    print(p)

    for n in p:
        print(n)

    # p 有的类似于一个list,在list还可以切片
    print("下标为5的值是:",p[5])
    print("下标为5-10的值是:",p[5:10])

    print(p.age)
    p.eat()

    p() #把person对象当成一个函数,直接调用,person对象 ==一个函数

    print(callable(p)) # 判断出来p对象,是不是可以调用的对象 (函数)

run:

Person object (name :张三)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
下标为5的值是: 8
下标为5-10的值是: [8, 13, 21, 34, 55]
18
eat函数执行
person 对象可以调用
True

六、枚举类

但是这样的定义的类型是 int ,并且仍然是变量,并且在运算中,无法时时创建对应的值,当然这种指代是以更好的方式去使用变量数值。
这里存在更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了 Enum 类来实现这个功能。

from enum import Enum

# 枚举中:一个名字 对应一个值 第一种:定义一个枚举
Month = Enum('Month',('jan','feb','mar','apr','may','jun','jul'))

# 把整个枚举中的所有值遍历出来
print(Month.__members__) # 枚举中的值自动从1开始,不会重复

#得到二月份的值
print(Month['feb'].value)

# 根据值(2)来得到月份的名字
print(Month(2).name)


# 定义一个颜色的常量枚举
class Color(Enum): # 第二种:自定义一个枚举类
    red = 100
    green =200
    blue =30
    yellow =200 # 不允许key相同或者value。如果value重复了根据value取name只能得到第一个

print(Color(200))

run:

{'jan': <Month.jan: 1>, 'feb': <Month.feb: 2>, 'mar': <Month.mar: 3>, 'apr': <Month.apr: 4>, 'may': <Month.may: 5>, 'jun': <Month.jun: 6>, 'jul': <Month.jul: 7>}
2
feb
Color.green
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周小唁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值