Python学习笔记(13)-类高级编程

1.类的属性和实例的属性

代码:

class Student():
    name = '糊渡娃'  # 这个属性是类的属性,但所有实例都可以访问

    def __init__(self, age=10):
        self.age = age  # 创建类后,实例独有的属性


s = Student()
print(s.name)  # 实例没有name属性,但是可以访问类的name属性
print((Student.name))  # 类的name属性
s.name = '刘德华'  # 实例新增name属性,覆盖掉了类的name属性
print(s.name)  # 访问的是实例的name属性
print((Student.name))
del s.name  # 删除实例的name属性
print(s.name)  # 再次访问,因为没有了name,所以访问的是类的name

print(dir(s))  # dir()函数,返回传入对象的所有属性,可以看到实例的独有属性age
print(dir(Student))

结果:(dir()返回所有属性太多,截取最后一部分)

糊渡娃
糊渡娃
刘德华
糊渡娃
糊渡娃
..., '__subclasshook__', '__weakref__', 'age', 'name']
..., '__subclasshook__', '__weakref__', 'name']

所以不建议把类的属性和实例的属性起相同的名字(吐槽下,敲代码起名字实在是太恶心了,尤其是英文不好的情况下(⊙﹏⊙))

2.动态增加实例的属性和方法及限制

python是动态语言,所以可以在实例创建后添加属性和方法,添加属性很简单,直接属性赋值即可。

代码:

class Student():
    pass


s = Student()
s.name = '大娃'

print(s.name)

还可以添加一个方法

代码:

from types import MethodType  # 添加方法要导入MethodType

class Student():
    pass

def set_age(self, age):  #  定义一个方法
    self.age = age

s = Student()
s.name = '大娃'
s.set_age = MethodType(set_age, s)  # 添加一个新属性(方法),传入set_age函数和s实例
s.set_age(18) # 调用函数,注意调用函数的时候同时给s实例新增加age属性

print(s.name)
print(s.age)

结果:

大娃
18

需要注意的是,对A实例添加的属性和方法,对B实例是无效的,若要所有实例都有效,则需要添加属性和方法到类中

代码:


...

def set_address(self, address):  # 定义新的方法
    self.address = address


Student.set_address = set_address  # 直接将函数名赋值给类需要添加的方法名即可
s1 = Student()
s2 = Student()  # 创建两个实例
s1.set_address('china')  # 调用方法
s2.set_address('American')
print(s1.address)
print(s2.address)

结果:

china
American

若要对可添加的属性进行限制,可使用特殊变量__slots__,例如
(PS:只限制实例的属性,类的属性不受限制)

代码:低版本可能需要继承object

class Student(object):
    __slots__ = ('name', 'age')  # 限制了实例,只能添加(或声明)name和age属性


s = Student()
s.name = '大娃'
s.age = 18
s.address = 'china'  # 此句会报错

__slots__变量对继承该类的子类是无效的,除非子类也声明了__slots__变量,此时,子类能添加的属性既有父类受限制的还有自身受限制的

3.@property代替get和set方法

之前记录了类中属性的set()get()方法,以防止外部随意的更改数据和无效的数据值,可用装饰器来简化代码,例如

代码:

class Student(object):
    @property
    def age(self):  # get()方法,只需要加上@property即可
        return self._age

    @age.setter
    def age(self, age): # set()方法,@get()方法的函数名.setter
        if isinstance(age, int) and 0 < age < 100: # 对传入的数据限制
            pass
        else:
            raise ValueError('错误的年龄') # 主动抛出异常,中断程序

        self._age = age


s = Student()
s.age = 19
#  s.age = 101
#  s.age = -1
print(s.age)

结果:

19
#  ValueError: 错误的年龄
#  ValueError: 错误的年龄

@property即为将函数声明为可读,@age.setter即为将函数声明为可写,可单独只声明只读,跟java类似

4.类的多继承

通过多重继承,一个子类可以同时获得多个父类的所有功能。

代码:

class Person():
    def say(self):
        print ('说话')


class Child():
    def study(self):
        print ('玩耍')


class Student(Person, Child):  # 多继承加逗号往后添加即可
    pass


s = Student()
s.say()  # 调用父类的say方法
s.study()  # 调用父类的study方法

结果:(如果继承的多个类中的方法同名,则执行第一个继承的类的方法)

说话
玩耍

这样通过多继承的方法来设计的程序叫做MixIn,一般把另外继承的类(即需要添加功能)名称加上MixIn,例如ChildMixIn,这样写程序的时候不需要考虑深层次的继承关系,只需要考虑功能组合,简化代码编写。
另:java不允许多继承

5.定制类

python里类似__xxx__这样的变量是有特殊用途的,比如说限制实例可添加属性的__slots__变量。以下列举其他几个特殊变量的使用

5.1__len__()

获取一个字符串的长度,使用函数len(),其实是调用了传入参数对象的内部的__len__()方法。例如
代码:

print (len('qwer'))

等价于

print ('qwer'.__len__())

所以只要在对象内定义__len()__函数,即可使用len()函数来调用该对象
代码:

class Person():
    def __init__(self, name=''):  # 构造函数中默认name=''
        self.name = name

    def __len__(self):  # 调用len()函数,该类的实例传入的时候,会调用此方法
        return len(self.name)


p1 = Person()
p2 = Person('Tom')
print (len(p1))  # 对象内部必须声明__len__()函数,否则无法调用len()
print (len(p2))

结果:

0
3

5.2__str__()

代码:

class Person():
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address

    def __str__(self):
        return '名字是' + self.name + ',年龄是' + str(self.age) + ',地址是' + self.address


p = Person('Jack', 18, 'China')
print (p)

结果:

名字是Jack,年龄是18,地址是China

如果去掉__str__()函数,则打印

<__main__.Person instance at 0x1068a5ab8>

其实就是跟java中的toString()方法类似

5.3__iter__()__next__()

列表和元组可以被用于for...in...循环,是因为对象内部有__iter__()__next__()方法,如果一个对象要想能够被for循环,可在类内部定义这两个方法。例如

代码:

class Person(object):
    def __init__(self, name=''):  # 自身属性list用于存放传进来的实例,i表示迭代次数
        self.name = name
        self.i = -1

    def addmember(self, *mem):
        self.list = mem

    def __iter__(self):  # 返回迭代的对象,自身就是迭代对象
        return self

    def __next__(self):  # for循环时调用next函数
        if self.i == len(self.list) - 1:  # 做迭代停止时的判断
            raise StopIteration()
        else:
            self.i += 1
            return self.list[self.i]  # 从存储的列表中取出实例返回,正序倒序可自己写逻辑


class Student(Person):  # 定义了3个类,都继承Person,都有name属性
    pass


class Teacher(Person):
    pass


class Trader(Person):
    pass


stu = Student('ZhangSan')
tea = Teacher('LiSi')
tra = Trader('WangWu')
p = Person()
p.addmember(stu, tea, tra)  # 将实例添加到p的属性list里

for mem in p:  # P定义了__iter__()和__next__(),所以可以被循环
    print (mem.name)

结果:

ZhangSan
LiSi
WangWu

5.4__getitem__

在上个代码中,如果使用p[0].name则会抛出错误信息TypeError: 'Person' object does not support indexing,此时Person只能循环,但不能像列表和元组一样用索引直接访问某个元素,此时,可在类中定义方法__getitem__()方法,例如
代码:(添加方法__getitem__())

    def __getitem__(self, item): # 第二个参数是下标,或者切片
        return self.list[item]
    ...

print (p[0].name)  # 此时即可用索引访问元素

结果:

ZhangSan

5.5__getattr__()

调用类的方法或者属性的时候,如果该方法或者属性不存在,就会调用类中的__getattr__()函数,需要这样功能的类,可定义此函数,并做相应处理

5.6__call__()

当调用实例方法或者属性的时候使用xxx.xxx或xxx.xxx(),其实实例也可以自己直接调用,例如xxx(),前提是是类内部定义了__call__()方法,例如

代码:

class Person():
    def say(self):
        print ('实例调用方法')

    def __call__(self, *args, **kwargs):
        print ('直接调用')


p = Person()
p.say()
p()

结果:

实例调用方法
直接调用

能被调用的对象都是一个Callable,判断是否是可调用对象用callable()函数,例如

代码:

    ...
print (callable(Person))  # Person类
print (callable(Person()))  # Person类的实例
print (callable(abs))  # abs()函数名(变量)
print (callable('asdf'))  

结果:

True
True
True
False

6.枚举类(3.0以上才有)

7.元类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值