property
1.property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
2.将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
property的机制
property也是一样的,首先要知道property本身是个类型,property()是这个类型的构造函数,@property装饰器,返回的property对象带有__get__,__set__和__delete__方法,会调用相应的fget, fset和fdelete。为了方便进一步构造,这个对象带有getter, setter, deleter方法,大致是这样的:
def setter(self, fset):
self.fset = fset
return self
也就是说只是进一步设置了其中的属性然后返回,所以.setter, .deleter都必须使用一致的名字。
Python当中取对象属性存在多种不同的机制,property属于descriptor机制,在Python当中是一种重要的机制
1.寻找obj.attr时。
2.如果有MyClass.__getattribute__定义则使用这个过程替代整个过程,不再进行后面的步骤
3.从对象的类中获取属性,如果类.__dict__中存在则直接返回.
# property用法
# 先从实例__dict__中寻找,再从类中寻找,在类中发现一个'sex': <property object>.优先
class People:
def __init__(self,name,age,SEX):
self.name = name
self.age = age
self.sex = SEX # 此步调用sex.setter,把SEX传给value,p.__sex = SEX
@property
def sex(self):
return self.__sex
# @sex.getter
# def sex(self):
# print("from getter")
# return self.__sex
@sex.setter
def sex(self,value):
if not isinstance(value,str):
raise TypeError(" 必须传字符串")
self.__sex = value # p.__sex = value
# print("sseetteerr")
@sex.deleter
def sex(self):
del self.__sex # del p.__sex
p = People("alex", "18", "male")
print(p.__dict__) # {'name': 'alex', 'age': '18', '_People__sex': 'male'}
print(People.__dict__) # 'sex': <property object>, '__init__': <function People.__init__>,'sex': <property object>
print(p.sex) # 先从p中找,再从People中找,找到'sex': <property object>。
p.sex = "female" # 实际调用sex.setter,可限制传入类型
print(p.sex) # male
del p.sex # 删除后报错
print(p.sex) # AttributeError: 'People' object has no attribute '_People__sex'
staticmethod静态方法
在类中定义的所有函数,都是对象的绑定方法。对象在调用时具有自动传值功能。
静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错
静态方法是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
用不同的方式创建Date实例
# 类的作用只有实例化和属性引用
# staticmethod为类提供了不同方式来实例化对象
import time
class Date:
def __init__(self,year, month, day):
self.year = year
self.month = month
self.day = day
@staticmethod
def now(): # 可以用Date.now()的形式创建一个实例,用的是今天的时间
print("------")
t = time.localtime()
r = Date(t.tm_year,t.tm_mon,t.tm_mday)
return r
@staticmethod
def tomorrow(): # 用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t = time.localtime(time.time() + 86400)
return Date(t.tm_year, t.tm_mon, t.tm_mday)
d1 = Date(1994,10,8)
d2 = Date.now()
d3 = Date.tomorrow()
print(d1.year,d1.month,d1.day) # 1994 10 8
print(d2.year,d2.month,d2.day) # 2017 4 21
print(d3.year,d3.month,d3.day) # 2017 4 22
print(d1.now) # 加了staticmethod,类型是<function Date.now>
print(d1.now) # 加staticmethod,类型是<bound method Date.now>,此时实例如果调用会传一个参数,报错
classmethod
__str__用法
str__定义在类内部,必须返回一个字符串类型,打印由这个类产生的对象时,会触发执行.创建类时,系统默认会实现__str_
# 打印由这个类产生的对象时,会触发执行
class Foo:
def __str__(self):
return "Foolish"
pass
f = Foo()
print(dir(f)) # 包含 '__str__',默认实现了__str__方法
print(f) # Foolish
l = list([1, 2, 3])
print(l) # [1, 2, 3],之所以能打印,也是通过执行__str__方法
print(l.__str__()) # [1, 2, 3]
classmethod类方法
类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法
# classmethod
import time
class Date:
def __init__(self,year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def now(cls):
print(cls)
t = time.localtime()
obj = cls(t.tm_year, t.tm_mon, t.tm_mday)
return obj
# @staticmethod
def tomorrow(): # 用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t = time.localtime(time.time() + 86400)
return Date(t.tm_year, t.tm_mon, t.tm_mday)
class EuroDate(Date):
def __str__(self):
return "%s年%s月%s日" % (self.year, self.month, self.day)
e1 = EuroDate.now()
e2 = EuroDate(1994,10,8)
e3 = EuroDate.tomorrow()
print(e1) # 2017年4月21日
print(e2) # 1994年10月8日
print(e3) # <__main__.Date object> e3是Date产生的实例,并不会调用EuroDate的__str__方法
总结
在类内部定义的函数无非三种用途
一:绑定到对象的方法
只要是在类内部定义的,并且没有被任何装饰器修饰过的方法,都是绑定到对象的
class Foo:
def test(self): #绑定到对象的方法
pass
def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常
pass
绑定到对象,指的是:就给对象去用,
使用方式:对象.对象的绑定方法(),不用为self传值
特性:调用时会把对象本身当做第一个参数传给对象的绑定方法
二:绑定到类的方法:classmethod
在类内部定义的,并且被装饰器@classmethod修饰过的方法,都是绑定到类的
class Foo:
def test(self): #绑定到对象的方法
pass
def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常
pass
绑定到对象,指的是:就给对象去用,
使用方式:对象.对象的绑定方法()
特性:调用时会把对象本身当做第一个参数传给对象的绑定方法
三:解除绑定的方法:staticmethod
既不与类绑定,也不与对象绑定,不与任何事物绑定
绑定的特性:自动传值(绑定到类的就是自动传类,绑定到对象的就自动传对象)
解除绑定的特性:不管是类还是对象来调用,都没有自动传值这么一说了
所以说staticmethod就是相当于一个普通的工具包
class Foo:
def test1(self):
pass
def test2():
pass
@classmethod
def test3(cls):
pass
@classmethod
def test4():
pass
@staticmethod
def test5():
pass
test1与test2都是绑定到对象方法:调用时就是操作对象本身
<function Foo.test1 at 0x0000000000D8E488>
<function Foo.test2 at 0x0000000000D8E510>
test3与test4都是绑定到类的方法:调用时就是操作类本身
<bound method Foo.test3 of <class 'main.Foo'>>
<bound method Foo.test4 of <class 'main.Foo'>>
test5是不与任何事物绑定的:就是一个工具包,谁来都可以用,没说专门操作谁这么一说
<function Foo.test5 at 0x0000000000D8E6A8>
homework
要求一:自定义用户信息数据结构,写入文件,然后读出内容,利用eval重新获取数据结构
with open('user.db','w') as write_file:
write_file.write(str({
"egon":{"password":"123",'status':False,'timeout':0},
"alex":{"password":"456",'status':False,'timeout':0},
}))
with open('user.db','r') as read_file:
data=read_file.read()
d=eval(data)
print(d['egon']['password']) # 123
print(d['egon']['status']) # False
print(d['egon']['timeout']) # 0
要求二:定义用户类,定义属性db,执行obj.db可以拿到用户数据结构
class User:
path = "user.db"
def __init__(self, username):
# with open(file_dir) as read_file:
self.name = username
@property
def db(self):
with open(self.path) as file:
info = eval(file.read())[self.name]
return info
@db.setter
def db(self,value):
with open(self.path,"w") as file:
file.write(str(value).strip())
file.flush()
u = User("egon")
print(u.db) # {'timeout': 0, 'password': 123, 'status': False}
u.db = {
"egon":{"password": 123, "status": False, "timeout": 123},
"alex": {"password":123, "status":False, "timeout":0}
}
print(u.db) # {'status': False, 'password': 123, 'timeout': 123}
作业三:登陆程序,登陆成功记录用户登陆状态,输错三次锁定用户10秒
import time
class User:
db_path='user.db'
def __init__(self,name):
self.name=name
@property # 设 置self.db为属性
def db(self):
with open(self.db_path,'r') as read_file:
info=read_file.read()
return eval(info)
@db.setter # 修改操作
def db(self,value): # self.db = value
with open(self.db_path,'w') as write_file:
write_file.write(str(value))
write_file.flush()
def login(self): # 登陆程序
data=self.db
if data[self.name]['status']:
print('已经登录')
return True
if data[self.name]['timeout'] < time.time(): # timeout值小于当前时间,则进行登陆
count=0
while count < 3:
passwd = int(input('password>>: ')) # 用户信息存的是数字
if not passwd:continue
if passwd == data[self.name]['password']: # 输入密码正确
data[self.name]['status']=True # 登陆成功修改用户信息
data[self.name]['timeout']=0
self.db=data # 写入文件
print("登录成功")
break
count+=1
else:
data[self.name]['timeout']=time.time()+10
self.db=data # # 输入错误延迟10秒写入文件
else:
print('账号已经锁定10秒')
def loggout(self): # 退出登陆
data = self.db
if not data[self.name]["status"]:
print("用户还没有登陆")
return 0
else:
data[self.name]["status"] = False
self.db = data
time.sleep(2)
print("%s退出成功" % self.name)
u1=User('egon')
u1.login() # 开始执行,用户输入密码,输入正确修改status为True,登陆成功,输错三次锁定用户10秒,
u1.loggout()
u2=User('alex')
u2.login()