zoukankan      html  css  js  c++  java
  • 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()
    
  • 相关阅读:
    codeforces484A
    codeforces559B
    spfa算法及判负环详解
    清北学堂提高组突破营考试T1
    lemon用法
    清北学堂提高组突破营游记day6
    清北学堂提高组突破营游记day5
    POJ-1679 The Unique MST (判断最小生成树的唯一性)
    HDU 4553 约会安排 (区间合并)【线段树】
    POJ 1236 Network Of Schools (思维+强连通)
  • 原文地址:https://www.cnblogs.com/zouruncheng/p/6745251.html
Copyright © 2011-2022 走看看