下面通过列子进行讲解:
1._下划线属性
Python3中 没有进行标记的内容就是公开的public
而_ (单下划线)进行标记的属性就是受保护的protected
__(双下划线)进行标记的属性就是私有的 private
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self.age = 2018-int(code[6:10]) #这里因为age 是可以通过身份证来获取age的
#所以,我们在init后面就没有在进行接收参数age
zhangsan = Person("张三","110101200010100909")
print(zhangsan.age)
#(输出)18
zhangsan.age = 30
print(zhangsan.age)
#(输出)30 #这里我们就可以看出,上面这种写法,age的值可以随意的更改
#但是一个人的年纪是不可以任意更改的
根据上面的问题,所以我做出如下操作:
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self._age = 2018-int(code[6:10]) #加入了下划线,
zhangsan = Person("张三","110101200010100909")
print(zhangsan.age)
zhangsan.age = 30
print(zhangsan.age)
#(输出)AttributeError: 'Person' object has no attribute 'age'
#报错了,没有找到age
print(zhangsan._age) #这里同样啊 加个下划线,发现输出了18,
#(输出18) #就相当于我们做了一个简单的隐藏,并能进行警告提示
zhangsan.age = 30 #但是你会发现没有下划线的zhangsan.age = 30是没有做出修改的
print(zhangsan._age)
#(输出)18
zhangsan._age = 30 #如果我们在这里也加入下划线,你就会发现,值还是修改了
print(zhangsan._age) #(输出)30
#数据不是绝对的安全,总有办法可以对其进行修改
而当我们对于age 进行__如下操作后
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self.__age = 2018-int(code[6:10])
return self.__age
zhangsan = Person("张三","110101200010100909")
print(zhangsan.__age)
zhangsan.__age = 30
print(zhangsan.__age)
#(输出)AttributeError: 'Person' object has no attribute '__age'
#你会发现啊,即使外面我们也用__ 来调用,age 还是报错,找不到__age.
#那么我们要怎么拿到age
怎么样拿到__双下划线的内容
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self.__age = 2018-int(code[6:10])
zhangsan = Person("张三","110101200010100909")
print(dir(zhangsan)) #dir 进行查看参数的属性列表
#(输出)['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'code', 'name']
#这里就看一看到可以查看的属性,会发现里面并没有__age属性
#但是会发现'_Person__age'这个属性,
#这个就是__age属性进行了一个简单加密
print(zhangsan._Person__age) #这里就可以输出age的值了
#(输出)18
在父类和子类中,父类如果有一个下划线的_属性,就是表明是收保护的,子类也可以进行调用,
但是对于有两个__的属性,表明是私有的属性,只有父类才可以进行操作,子类和外界不可以进行操作和调用。
但是有时候我们是想让外界能用和查看,但是不能进行一个修改,所以我们进行如下操作
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self._age = 2018-int(code[6:10])
def getage(self): #用函数进行封装,然后用return返回出去
return self._age
zhangsan = Person("张三","110101200010100909")
print(zhangsan.getage())
#(输出)18
#但是我们实际上是调用的def getage(self):这个方法,并不是调用数据zhangsan.age
#那么我们怎做呢?
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self._age = 2018-int(code[6:10])
@property #装饰器
def age(self): #将age函数 进行一个装饰 ,
#可以让age像调用数据(zhangsan.age)一样调用
#而不用像前面讲的那样调用方法,主要就是见名知意
return self._age # def age(self): 有人会问为什么这里用age命名啊?
#因为我们要用zhangsan.age的语法进行调用,我们只是将
#调用方法 def getage(self):的形式装饰成赋值zhangsan.age= 的形式
#如果不用age命名,不能达到我们想要的效果
zhangsan = Person("lisi", "110101200010100909")
print(zhangsan.age)
#(输出)18
zhangsan.age = 10 #但是外界这样是不可以修改的
print(zhangsan.age)
#(输出) AttributeError: can't set attribute #报错了,不能设置属性
zhangsan._age = 10
print(zhangsan.age) #但是还是用下划线进行修改的话还是可以进行修改的
#(输出)18 #只是说 用了障眼法的效果,简单的处理一下
#(输出)10
@property 就是用来装饰函数,变成了属性,我们可以使用正常的点符号访问它,
property取代setter和getter方法。把get方法变为属性只需要加上@property装饰器即可,此时@property本身又会创建另外一个装饰器@age.setter,负责把set方法变成给属性赋值,这么做完后,我们调用起来既可控又方便.
下面通过列子进行讲解:
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self._age = 2018-int(code[6:10])
@property
def age(self):
return self._age
zhangsan = Person("lisi", "110101200010100909")
print(zhangsan.age)
#(输出)18
zhangsan.code = "110101200210100909" #我们预想的是age自动通过省份证code进行自我更改
print(zhangsan.age) #但是这里可以看出age并没有进行预期的效果
#(输出)18 #因为这里self._age = 2018-int(code[6:10])
#只计算了一次,因为你只是修改了code,
#并没有执行self._age = 2018-int(code[6:10])
#而调用zhangsan.age还是返回的是原来的age18
那么我们怎么操作?
class Person:
def __init__(self, name, code):
self.name = name
self.code = code
self._age = 2018-int(code[6:10])
@property
def age(self):
return self._age
def setcode(self,code): #这里我们添加一个setcode方法,接收self,code,
self.code = code #修改init里面的self.code
self._age = 2018 - int(code[6:10]) #修改init里面的self._age
zhangsan = Person("lisi", "110101200010100909")
print(zhangsan.age) #(输出)18
zhangsan.code = "110101200210100909"
print(zhangsan.age) #(输出)18
zhangsan.setcode("110101200210100909")
print(zhangsan.age) #(输出)16 #可以看出age按照预想的输出了
#zhangsan.setcode("110101200210100909")但是这种操作不是很直观,并不能看出是进行一个赋值修改的操作。
class Person:
def __init__(self, name, code):
self.name = name
self._code = code
self._age = 2018-int(code[6:10])
@property
def age(self):
return self._age
# def setcode(self,code): #这里注释了上面的情况,方便对照
# self.code = code
# self._age = 2018 - int(code[6:10])
# 这里return self._code如果是return self.code你就会发现
@property #他会是一个死循环,因为你返回一个self.code
def code(self): #是不是又调用了 def code(self, code):
return self._code #导致又进入这个函数,上面的age也是一个道理
# 所以我们进行一个self._code的处理,init里面也对应修改
@code.setter #为什么又写一个@property def code(self, code):
def code(self,code): #因为既然我们要写入一个数据,首先要进行读取一个数据
self._code = code #一个@property 对应一个@code.setter,并且是同样的对code进行操作
self._age = 2018 - int(code[6:10])
zhangsan = Person("lisi", "110101200010100909")
print(zhangsan.age) #(输出)18
zhangsan.code = "110101200210100909"
print(zhangsan.age) #(输出)16
#一个@property 对应一个@code.setter,并且是同样的相同的属性进行操作
@property 进行对属性的装饰,让下划线的属性(只读属性)可以正常用 .(属性) 的方式进行调取,
@code.setter对数据进行修改并装饰,让 下划线的属性 (只读属性)可以正常用 .(属性) = 的方式进行赋值