python讲稿4_2 类2 单下划线,双下划线

  1. Python 用下划线作为变量前缀和后缀指定特殊变量

分三种:

  • 单下划线开始的变量,如_xxx,表示不能用’from module import *'导入
  • 双下划线开始的变量,如__xxx,表示类中的私有变量名
  • 前后各有双下划线的变量,如__xxx__,表示系统定义名字,也叫magic variable(魔术变量)
class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def get_name(self):
        return self.__name
    def set_name(self,name):
        self.__name=name
s1=Student('xf1',21,'harbin')
print(s1._age)
print(s1.addr)

可以看到_age和addr可以直接访问,但__name不能直接访问.

>>>print(s1.__name)
AttributeError: 'Student' object has no attribute '__name'
>>>print(s1.get_name())
xf1

上面提示__name找不到,其实Student类将__name重命名为_Student__name,看下面:

>>>s1.__dict__
{'_Student__name': 'xf1', '_age': 21, 'addr': 'harbin'}
>>>print(s1._Student__name)
xf1

2 魔术方法(__xxx__)
可以分为以下几类:

  • 2.1 类的基础方法
  • 2.2 计算属性
  • 2.3 可比较的类
  • 2.4 行为方式与迭代器类似的类
  • 2.5 可序列化的类(未讲)
  • 2.6 可在 with 语块中使用的类(未讲)

2.1 类的基础方法

这里介绍__str__()和__repr__,先看一个例子:

class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
s1=Student('xf1',21,'harbin')
print(s1)

语句print(s1)打印对象s1,输出结果为:

<__main__.Student object at 0x000001AFBA0D34A8>

现在我们在Student中添加__str__方法如下:

class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def __str__(self):
        return '(Student:%s,%s,%s)'%(self.__name,self._age,self.addr)
s1=Student('xf1',21,'harbin')
print(s1)

结果如下:

(Student:xf1,21,harbin)

但是:直接输入s1,结果仍不友好.

>>>s1
<__main__.Student at 0x1afba0d3710>

下面介绍__repr__方法:

class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def __str__(self):
        return '(Student:%s,%s,%s)'%(self.__name,self._age,self.addr)
    def __repr__(self):
        return '(Student info:%s,%s,%s)'%(self.__name,self._age,self.addr)
s1=Student('xf1',21,'harbin')
s1
结果为:
(Student info:xf1,21,harbin)

注: 大家自己总结__str__()和__repr__()的区别.

为什么每个类都最好有一个 repr 方法
如果你没有添加 str 方法,Python 在需要该方法但找不到的时候,它会去调用 repr 方法。因此,我推荐在写自己的类的时候至少添加一个 repr 方法,这能保证类到字符串始终有一个有效的自定义转换方式。

2.2 计算属性

2.2.1 __setattr__ 拦截 属性的的赋值语句 (obj.xx = xx),可以替代set函数

class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def __setattr__(self,key,value):
        print('__setattr__ called')
        self.__dict__[key]=value
s1=Student('xf1',21,'harbin')
s1._Student__name='xf2'

结果为:

__setattr__ called
__setattr__ called
__setattr__ called
__setattr__ called

2.2.2 __getattribute__和__getattr__

__getattribute__拦截运算(obj.xx),当每次调用属性时,python会无条件进入__getattribute__中,不论属性存在与否.

__getattr__是在查找不到属性时调用,当obj.xx找不到属性xx时,__getattr__被调用.

class Student:
    def __init__(self,name='空',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def __setattr__(self,key,value):
        print('__setattr__ called')
        self.__dict__[key]=value
    def __getattribute__(self,*args,**kwargs):
        print('__getattribute__ called')
        return object.__getattribute__(self,*args,**kwargs)
    def __getattr__(self,item):
        return 'not found '+item
s1=Student('xf1',21,'harbin')
s1._Student__name='xf2'
s1.gg

注意: 1 在__getattribute__和__getattr__中不用使用类似 self.*** 这种调用方式因为 每次调用类的属性都会强制调用 __getattribute__ ,所以极有可能造成递归调用.
2 访问属性的时候首先会调用__getattribute__ 方法,这个方法会检测__dict__ 里面有没有这个属性,没有的话最后再调用一下 __getattr__.

2.2.3 其他的一些magic method

目的所编写代码python实际调用
序列的长度len(s)s.__len__()
是否包含特定值x in ss.__contains__(x)
通过键来获取值x[key]x.__getitem__(key)
通过键来设置值x[key]=valuex.__setitem__(key,value)
class Dog:
    def __init__(self,name1,age1,desc):
        self.name = name1
        self.age = age1
        self.desc = desc
    def __len__(self):
        return len(self.name)
    def __getitem__(self,key):
        if key in self.__dict__:
            return self.__dict__[key]
    def __setitem__(self,key,value):
        self.__dict__[key] = value

dog1 = Dog('join_merry',2,'my friend')
len(dog1)

dog1['age'] = 3
dog1['age']
dog1['speces'] = '2ha'
dog1.__dict__

再看一个例子。
例1 Stu类

class Stu:
    def __init__(self,name='',age=None,addr=None):
        self.__name=name
        self._age=age
        self.addr=addr
    def get_name(self):
        return self.__name
    def set_name(self,name):
        self.__name=name

    def __repr__(self):
        return '(Student info:%s,%s,%s)'%(self.__name,self._age,self.addr)
    def __len__(self):
        return len(self.__name)
    def __getitem__(self,key):
        if key=='__name':
            return self.__name
        elif key=='_age':
            return self._age
        elif key=='addr':
            return self.addr

s1=Stu('xf21',21,'harbin')

s1['__name'],s1['_age'],s1['addr'],len(s1)

结果为(‘xf21’, 21, ‘harbin’, 4).

2.3 可比较的类

目的所编写代码python实际调用
相等x==yx.__eq__(y)
不相等x!=yx.__ne__(y)
小于x<yx.__lt__(y)
小于或等于x<=yx.__le__(y)
大于x>yx.__gt__(y)
大于或等于x>=yx.__ge__(y)
上下文环境中的真值if x:x.__bool__()

2.4 迭代器和可迭代对象

例2 自定义字符串类MyStr(本身也是迭代器),以及可迭代对象MyStr_iter.

class MyStr: #这是一个迭代器,含有__iter__,__next__
    import copy
    def __init__(self,data):
        self.__data=data
        self.__index=0
    def __repr__(self):
        return self.__data
    def __len__(self):
        return len(self.__data)
    def __contains__(self,x):       # x in str1
        if x in self.__data:
            return True
        else:
            return False
    def __getitem__(self,key):      # str1[0],str1[1],...

        for i in range(len(self)):
            if key==i:
                return self.__data[i]
    def __setitem__(self,key,value): # str1[0]=value1
        if key=='__data':
            self.__data=value
        for i in range(len(self)):
            if key==i:
                t=list(self.__data)
                t[i]=value
                self.__data=''.join(t)
    #运算符重载
    def __setattr__(self,key,value): # 赋值运算符=
        #print('__setattr__ called')
        self.__dict__[key]=value
    def __add__(self,s):             # 加法+
        self.__data=self.__data+s.__data
        return self
    #添加迭代器,需要__iter__,__next__
    def __iter__(self):
        return copy.copy(self)
    def __next__(self):
        if self.__index < len(self):
            res=self.__data[self.__index]
            self.__index+=1
            return res
        else:
            raise StopIteration
            
class MyStr_iter: #创建一个类,用来实例化可迭代对象,可迭代对象只有__iter__方法
    def __init__(self,data):
        self.__data=data
    def __iter__(self):
        return MyStr(self.__data)    

下面是使用举例.

ms1_iter=MyStr_iter("hello")
for i in ms1_iter:
    print(i,end=',')

结果为h,e,l,l,o,.

参考文献

  1. https://www.cnblogs.com/nkwy2012/p/6264031.html
  2. http://python.jobbole.com/88582/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值