前言

接着上一篇笔记,我们来看看内置装饰器propertystaticmethodclassmethod

一、property装饰器

1. 普通方式修改属性值
  • code
  1. class Celsius:
  2. def __init__(self, temperature = 0):
  3. self.temperature = temperature
  4. def to_fahrenheit(self):
  5. return (self.temperature * 1.8) + 32
  6. man = Celsius(-280)
  7. print('dirctly get:', man.temperature)
  8. print('to_fahrenheit', man.to_fahrenheit())
  9. # 查看示例的属性
  10. print(man.__dict__)
  • output
  1. dirctly get: -280
  2. to_fahrenheit: -472.0
  3. {'temperature': -280}

这里直接通过实例来修改类的属性值,但从输出可以看到温度超过了绝对零度-273.15却没有保错,这是一个bug,下面来修复下。

2. 使用get_xxx, set_xxx方法修改类的属性值
  • code
  1. class Celsius:
  2. def __init__(self, temperature = 0):
  3. self.set_temperature(temperature)
  4. def to_fahrenheit(self):
  5. return (self.get_temperature() * 1.8) + 32
  6. # new update
  7. def get_temperature(self):
  8. return self._temperature
  9. def set_temperature(self, value):
  10. if value < -273:
  11. raise ValueError("Temperature below -273 is not possible")
  12. self._temperature = value
  13. # 超过绝对零度报错
  14. # man = Celsius(-280)
  15. man = Celsius(-28)
  16. # temperature改为_temperature了
  17. #print('dirctly get:', man.temperature)
  18. print('dirctly get:', man._temperature)
  19. print('use get_temperature get:', man.get_temperature())
  20. print('to_fahrenheit:', man.to_fahrenheit())
  21. # 查看示例的属性
  22. print(man.__dict__)
  • output
  1. dirctly get: -28
  2. use get_temperature get: -28
  3. to_fahrenheit: -18.4
  4. {'_temperature': -28}

这里用一个私有变量_temperature来对temperature做限制。新增了get_xxx, set_xxx方法来修复了bug,但这样的话用到了这个类的代码都要修改了,获取温度值要将obj.temperature修改为obj.get_temperature(),修改温度值要将obj.temperature = val修改为obj.set_temperature(val)。如果有成千上万行代码用到这个类的话,显然这样修改是不行的,下面使用@property来解决。

3. 使用@property装饰器修改类的属性值
  • code
  1. class Celsius:
  2. def __init__(self, temperature = 0):
  3. # 不知道是不是作者写多了‘_’下划线,如果用私有变量_temperature的话,
  4. # 在类初始化时就赋值并不会调用setter判断温度是否大于-273度,所以还会出现bug
  5. # self._temperature = temperature
  6. self.temperature = temperature
  7. def to_fahrenheit(self):
  8. return (self.temperature * 1.8) + 32
  9. @property
  10. def temperature(self):
  11. print("Getting value")
  12. return self._temperature
  13. @temperature.setter
  14. def temperature(self, value):
  15. print('vvvvvv', value)
  16. if value < -273:
  17. raise ValueError("Temperature below -273 is not possible")
  18. print("Setting value")
  19. self._temperature = value
  20. man = Celsius(-28)
  21. print('dirctly get:', man.temperature)
  22. # man.temperature(-28)会报错:TypeError: 'int' object is not callable
  23. # 使用property后temperture(self, value)只能通过属性赋值方式更改
  24. # print('use @temperature.setter:', man.temperature(-28))
  25. man.temperature = -88
  26. print('to_fahrenheit:', man.to_fahrenheit())
  27. print(man.__dict__)
  • output
  1. vvvvvv -28
  2. Setting value
  3. Getting value
  4. dirctly get: -28
  5. vvvvvv -88
  6. Setting value
  7. Getting value
  8. to_fahrenheit: -126.4
  9. {'_temperature': -88}

@property装饰器就是调用了python内置方法property(fget=None, fset=None, fdel=None, doc=None),上面@temperature.setter就相当于fset=temperature方法。使用了@property装饰器,就可以在不影响原有代码的情况下修改类的属性值

  • 参考文章

https://www.programiz.com/python-programming/property

二、staticmethod装饰器(静态方法)

静态方法的用得很少,当需要一个不访问类的任何属性,但它属于该类的实用函数时就可以用静态方法,它只处理传入的参数。比如下面的静态方法就单纯接收一个参数date更改下它的连接符就返回了,但因为跟类Dates放一起比较好归类管理才放到类里面的。可以用类直接调用静态方法,也可以用类的实例调用静态方法

  • code
  1. class Dates:
  2. def __init__(self, date):
  3. self.date = date
  4. def getDate(self):
  5. return self.date
  6. @staticmethod
  7. def toDashDate(date):
  8. return date.replace("/", "-")
  9. date = Dates("15-12-2016")
  10. dateFromDB = "15/12/2016"
  11. # 用类直接调用静态方法
  12. dateWithDash = Dates.toDashDate(dateFromDB)
  13. # 用类的实例调用静态方法
  14. dateWithDash_1 = date.toDashDate(dateFromDB)
  15. if(date.getDate() == dateWithDash):
  16. print("Equal")
  17. if(date.getDate() == dateWithDash_1):
  18. print("Equal_1")
  19. else:
  20. print("Unequal")
  • output
  1. Equal
  2. Equal_1
  • 参考文章

https://www.programiz.com/python-programming/methods/built-in/staticmethod

三、classmethod装饰器(类方法)

  • code
  1. from datetime import date
  2. # random Person
  3. class Person:
  4. def __init__(self, name, age):
  5. self.name = name
  6. self.age = age
  7. @classmethod
  8. def fromBirthYear(cls, name, birthYear):
  9. return cls(name, date.today().year - birthYear)
  10. def display(self):
  11. print(self.name + "'s age is: " + str(self.age))
  12. person = Person('Adam', 19)
  13. person.display()
  14. person1 = Person.fromBirthYear('John', 1985)
  15. person1.display()
  • output
  1. Adam's age is: 19
  2. John's age is: 34

从上面的例子可以看到,类方法需要有一个必需参数cls,而不是self(也可以是其他名字,但约定俗成用cls),因为类方法的参数cls就是这个类本身。,所以cls(name, date.today().year - birthYear)等同于调用了Person(name, date.today().year - birthYear)当我们调用的类方法return cls(arg1,arg2),因为类方法的参数cls就是这个类本身,所以就会将这些参数赋值给类的__init__函数再次初始化

有些童鞋应该发现了上面的示例也可以用静态方法来实现,但这样的话它创建的实例就硬编码为只属于基类了(当我们不希望类的子类更改/重写方法的特定实现时,静态方法就派上用场了)。也就是用确定的一个类Person代替了cls,这样就是硬编码创建的实例只属于基类Person了。如果有类继承基类的话,使用静态方法创建的实例只属于基类,继承了基类的子类都不属于实例,而使用类方法的话,继承了基类的子类创建的实例,既属于基类也属于继承了基类的子类。看下面代码就懂了。

  • code
  1. from datetime import date
  2. # random Person
  3. class Person:
  4. def __init__(self, name, age):
  5. self.name = name
  6. self.age = age
  7. @staticmethod
  8. def fromBirthYearStaticmethod(name, birthYear):
  9. return Person(name, date.today().year - birthYear)
  10. @classmethod
  11. def fromBirthYearClassmethod(cls, name, birthYear):
  12. return cls(name, date.today().year - birthYear)
  13. def display(self):
  14. print(self.name + "'s age is: " + str(self.age))
  15. class Man(Person):
  16. sex = 'Male'
  17. class MoreMan(Man):
  18. sex = 'Unknown'
  19. man = Man.fromBirthYearClassmethod('John', 1985)
  20. print('使用类方法')
  21. man.display()
  22. print('man是类Man的实例吗', isinstance(man, Man))
  23. print('man是类Person的实例吗', isinstance(man, Person))
  24. man1 = Man.fromBirthYearStaticmethod('John', 1985)
  25. print('使用静态方法')
  26. man1.display()
  27. print('man1是类Man的实例吗', isinstance(man1, Man))
  28. print('man1是类Person的实例吗', isinstance(man1, Person))
  29. man2 = MoreMan.fromBirthYearClassmethod('John', 1985)
  30. print('使用类方法')
  31. man2.display()
  32. print('man2是类Man的实例吗', isinstance(man2, Man))
  33. print('man2是类MoreMan的实例吗', isinstance(man2, MoreMan))
  34. print('man2是类Person的实例吗', isinstance(man2, Person))
  35. man3 = MoreMan.fromBirthYearStaticmethod('John', 1985)
  36. print('使用静态方法')
  37. man3.display()
  38. print('man3是类Man的实例吗', isinstance(man3, Man))
  39. print('man3是类MoreMan的实例吗', isinstance(man3, MoreMan))
  40. print('man3是类Person的实例吗', isinstance(man3, Person))
  • output
  1. 使用类方法
  2. John's age is: 34
  3. man是类Man的实例吗 True
  4. man是类Person的实例吗 True
  5. 使用静态方法
  6. John's age is: 34
  7. man1是类Man的实例吗 False
  8. man1是类Person的实例吗 True
  9. 使用类方法
  10. John's age is: 34
  11. man2是类Man的实例吗 True
  12. man2是类MoreMan的实例吗 True
  13. man2是类Person的实例吗 True
  14. 使用静态方法
  15. John's age is: 34
  16. man3是类Man的实例吗 False
  17. man3是类MoreMan的实例吗 False
  18. man3是类Person的实例吗 True

从上面的输出就可以看出来了,虽然静态方法也可以实现功能,但在继承的时候却没有继承给子类。所以如果一个方法需要被继承的子类使用的话还是用类方法。相反,当我们不希望类的子类更改/重写方法的特定实现时,就用静态方法。

  • 参考文章

https://www.programiz.com/python-programming/methods/built-in/classmethod

四、综合比较

实例方法,类方法,静态方法的综合比较,异同见代码注释。

  • code
  1. class C():
  2. # instance method
  3. def ins(self):
  4. print("instance method")
  5. @staticmethod
  6. def sta():
  7. print("static method")
  8. @classmethod
  9. def cla(cls, c_arg):
  10. print("class method %s" % c_arg)
  11. # 实例化类C()
  12. ins_cls = C()
  13. # 实例方法只能用实例调用,不可用直接用类调用,会报错:“TypeError: ins() missing 1 required positional argument: 'self'”
  14. # C.ins()
  15. ins_cls.ins()
  16. # 静态方法可以通过类调用,也可以通过实例调用
  17. C.sta()
  18. ins_cls.sta()
  19. # 类方法可以通过类调用,也可以通过实例调用cla(cls)中的参数cls就是调用它的类本身,cls跟self差不多,用的时候只需传实际参数c_arg即可
  20. C.cla("class_arg")
  21. ins_cls.cla("class_arg")
  • output
  1. instance method
  2. static method
  3. static method
  4. class method
  5. class method

公众号往期文章

  • python装饰器
  • scrapy-redis debug视频
  • scrapy-redis源码浅析
  • scrapy过滤重复数据和增量爬取
  • redis基础笔记
  • scrapy电影天堂实战(二)创建爬虫项目
  • scrapy电影天堂实战(一)创建数据库
  • scrapy基础笔记
  • 在docker镜像中加入环境变量
  • 笔记 | mongodb 入门操作
  • 笔记 | python元类
  • 笔记 | python2和python3使用super()
  • 那些你在python3中可能没用到但应该用的东西
  • superset docker 部署
  • 开机启动容器里面的程序
  • 博客 | 三步部署hitchhiker-api

python内置装饰器的更多相关文章

  1. Python 内置装饰器

    内置的装饰器 ​ 内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些. @property ​ 在了解这个装饰器前,你需要知道在不使用装饰器怎么写一个属性. d ...

  2. Python内置装饰器@property

    在<Python装饰器(Decorators )>一文中介绍了python装饰器的概念,日常写代码时有一个装饰器很常见,他就是内置的@property. 我们一步步的来接近这个概念. 一个 ...

  3. 面向对象之classmethod和staticmethod(python内置装饰器)

    对象的绑定方法复习classmethodstaticmethod TOC 对象的绑定方法复习 由对象来调用 会将对象当做第一个参数传入 若对象的绑定方法中还有其他参数,会一并传入 classmetho ...

  4. python进阶04 装饰器、描述器、常用内置装饰器

    python进阶04 装饰器.描述器.常用内置装饰器 一.装饰器 作用:能够给现有的函数增加功能 如何给一个现有的函数增加执行计数的功能 首先用类来添加新功能 def fun(): #首先我们定义一个 ...

  5. python基础语法16 面向对象3 组合,封装,访问限制机制,内置装饰器property

    组合: 夺命三问: 1.什么是组合? 组合指的是一个对象中,包含另一个或多个对象. 2.为什么要用组合? 减少代码的冗余. 3.如何使用组合? 耦合度: 耦: 莲藕 ---> 藕断丝连 - 耦合 ...

  6. classmethod、staticclassmethod内置装饰器函数

    # method 英文是方法的意思 # classmethod 类方法 # 当一个类中的方法中只涉及操作类的静态属性时,此时在逻辑上,我们想要直接通过类名就可以调用这个方法去修改类的静态属性,此时可以 ...

  7. property内置装饰器函数和@name.setter、@name.deleter

    # property # 内置装饰器函数 只在面向对象中使用 # 装饰后效果:将类的方法伪装成属性 # 被property装饰后的方法,不能带除了self外的任何参数 from math import ...

  8. python基础--定义装饰器(内置装饰器)

    装饰器的定义: 装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景中,比如-- >插入 ...

  9. python基础-内置装饰器classmethod和staticmethod

    面向对象编程之classmethod和staticmethod classmethod 和 staticmethod都是python内置的装饰器 classmethod 的作用:给在类内部定义的方法装 ...

随机推荐

  1. LCD应用程序测试

    #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl ...

  2. HNUSTOJ-1600 BCD时钟

    1600: BCD时钟 时间限制: 1 Sec  内存限制: 128 MB提交: 1038  解决: 156[提交][状态][讨论版] 题目描述 BCD码是指用四位二进制数来表示十进制数中的0~9这十 ...

  3. Linux 重定向命令有哪些?有什么区别?

    1.重定向>Linux 允许将命令执行结果重定向到一个文件,本应显示在终端上的内容保存到指定文件中.如:ls >test.txt ( test.txt 如果不存在,则创建,存在则覆盖其内容 ...

  4. 【数据库运维】数据库(server)的时区设置及世界主要地区的时区

    [时区设置不当会有什么问题] 当进行海外项目运维的时候,常常会遇到时区设置的问题.假设时区设置不当 或者 同样项目的server之间的时区不一致,都会有导致项目的数据异常的风险. 假设数据表的字段使用 ...

  5. TypeScript从入门到Vue项目迁移

    1. 前言 ES6的普及,大大简化了JavaScript的表达方式 大型项目中,js没有类型检查.表达方式灵活,多人协作代码调试和维护成本高 2. 定义 TypeScript 是 JavaScript ...

  6. 第一个javascript脚本

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 遍历并读取指定目录下的所有文件内容,写入Map集合然后输出在控制台和本地文件

    public class FileWrite { public static void main(String[] args) throws Exception { //封装数据源目录 File sr ...

  8. VUE CLI3.X 创建项目

    Node.js环境搭建 Node.js基于V8引擎,可以让js代码脱离浏览器运行 Vue CLI3.0 需要Node.js 8.9或者更高版本. 用nvm或者nvm-windows在同一台电脑中管理多 ...

  9. java 生成时机时间

    import java.text.SimpleDateFormat;  import java.util.Date;  import java.util.Random;  public class t ...

  10. c++函数参数或返回值为函数指针

    C++中函数指针的形式为:返回值类型 + 参数类型,函数没有值类型,但是却可以声明函数的指针,因为函数是可寻址的,存放在内存中的代码段,可以从指针访问. 函数指针可以声明为: void (*pF)(v ...