property、staticmethod和classmethod

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()
原文地址:https://www.cnblogs.com/zouruncheng/p/6745251.html