前言

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

一、property装饰器

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

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

2. 使用get_xxx, set_xxx方法修改类的属性值
  • code
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature) def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32 # new update
def get_temperature(self):
return self._temperature def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value # 超过绝对零度报错
# man = Celsius(-280)
man = Celsius(-28)
# temperature改为_temperature了
#print('dirctly get:', man.temperature)
print('dirctly get:', man._temperature)
print('use get_temperature get:', man.get_temperature())
print('to_fahrenheit:', man.to_fahrenheit())
# 查看示例的属性
print(man.__dict__)
  • output
dirctly get: -28
use get_temperature get: -28
to_fahrenheit: -18.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
class Celsius:
def __init__(self, temperature = 0):
# 不知道是不是作者写多了‘_’下划线,如果用私有变量_temperature的话,
# 在类初始化时就赋值并不会调用setter判断温度是否大于-273度,所以还会出现bug
# self._temperature = temperature
self.temperature = temperature def to_fahrenheit(self):
return (self.temperature * 1.8) + 32 @property
def temperature(self):
print("Getting value")
return self._temperature @temperature.setter
def temperature(self, value):
print('vvvvvv', value)
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value man = Celsius(-28)
print('dirctly get:', man.temperature) # man.temperature(-28)会报错:TypeError: 'int' object is not callable
# 使用property后temperture(self, value)只能通过属性赋值方式更改
# print('use @temperature.setter:', man.temperature(-28))
man.temperature = -88 print('to_fahrenheit:', man.to_fahrenheit())
print(man.__dict__)
  • output
vvvvvv -28
Setting value
Getting value
dirctly get: -28
vvvvvv -88
Setting value
Getting value
to_fahrenheit: -126.4
{'_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
class Dates:
def __init__(self, date):
self.date = date def getDate(self):
return self.date @staticmethod
def toDashDate(date):
return date.replace("/", "-") date = Dates("15-12-2016")
dateFromDB = "15/12/2016"
# 用类直接调用静态方法
dateWithDash = Dates.toDashDate(dateFromDB)
# 用类的实例调用静态方法
dateWithDash_1 = date.toDashDate(dateFromDB) if(date.getDate() == dateWithDash):
print("Equal")
if(date.getDate() == dateWithDash_1):
print("Equal_1")
else:
print("Unequal")
  • output
Equal
Equal_1
  • 参考文章

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

三、classmethod装饰器(类方法)

  • code
from datetime import date

# random Person
class Person:
def __init__(self, name, age):
self.name = name
self.age = age @classmethod
def fromBirthYear(cls, name, birthYear):
return cls(name, date.today().year - birthYear) def display(self):
print(self.name + "'s age is: " + str(self.age)) person = Person('Adam', 19)
person.display() person1 = Person.fromBirthYear('John', 1985)
person1.display()
  • output
Adam's age is: 19
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
from datetime import date

# random Person
class Person:
def __init__(self, name, age):
self.name = name
self.age = age @staticmethod
def fromBirthYearStaticmethod(name, birthYear):
return Person(name, date.today().year - birthYear) @classmethod
def fromBirthYearClassmethod(cls, name, birthYear):
return cls(name, date.today().year - birthYear) def display(self):
print(self.name + "'s age is: " + str(self.age)) class Man(Person):
sex = 'Male' class MoreMan(Man):
sex = 'Unknown' man = Man.fromBirthYearClassmethod('John', 1985)
print('使用类方法')
man.display()
print('man是类Man的实例吗', isinstance(man, Man))
print('man是类Person的实例吗', isinstance(man, Person)) man1 = Man.fromBirthYearStaticmethod('John', 1985)
print('使用静态方法')
man1.display()
print('man1是类Man的实例吗', isinstance(man1, Man))
print('man1是类Person的实例吗', isinstance(man1, Person)) man2 = MoreMan.fromBirthYearClassmethod('John', 1985)
print('使用类方法')
man2.display()
print('man2是类Man的实例吗', isinstance(man2, Man))
print('man2是类MoreMan的实例吗', isinstance(man2, MoreMan))
print('man2是类Person的实例吗', isinstance(man2, Person)) man3 = MoreMan.fromBirthYearStaticmethod('John', 1985)
print('使用静态方法')
man3.display()
print('man3是类Man的实例吗', isinstance(man3, Man))
print('man3是类MoreMan的实例吗', isinstance(man3, MoreMan))
print('man3是类Person的实例吗', isinstance(man3, Person))
  • output
使用类方法
John's age is: 34
man是类Man的实例吗 True
man是类Person的实例吗 True
使用静态方法
John's age is: 34
man1是类Man的实例吗 False
man1是类Person的实例吗 True
使用类方法
John's age is: 34
man2是类Man的实例吗 True
man2是类MoreMan的实例吗 True
man2是类Person的实例吗 True
使用静态方法
John's age is: 34
man3是类Man的实例吗 False
man3是类MoreMan的实例吗 False
man3是类Person的实例吗 True

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

  • 参考文章

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

四、综合比较

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

  • code
class C():
# instance method
def ins(self):
print("instance method")
@staticmethod
def sta():
print("static method")
@classmethod
def cla(cls, c_arg):
print("class method %s" % c_arg) # 实例化类C()
ins_cls = C() # 实例方法只能用实例调用,不可用直接用类调用,会报错:“TypeError: ins() missing 1 required positional argument: 'self'”
# C.ins()
ins_cls.ins()
# 静态方法可以通过类调用,也可以通过实例调用
C.sta()
ins_cls.sta()
# 类方法可以通过类调用,也可以通过实例调用cla(cls)中的参数cls就是调用它的类本身,cls跟self差不多,用的时候只需传实际参数c_arg即可
C.cla("class_arg")
ins_cls.cla("class_arg")
  • output
instance method
static method
static method
class method
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. chineseocr项目的配置阶段出现的问题及解决方案

    chineseocr为GitHub上的一个开源项目,主要使用yolos,crnn等深度学习框架训练好后的模型使用.测试结果发现,不管是针对文本文件.表格文件.还是场景图,如身份证火车票,识别效果都比较 ...

  2. Python基础-1 python由来 Python安装入门 注释 pyc文件 python变量 获取用户输入 流程控制if while

    1.Python由来 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚 ...

  3. tomcat 端口8080占用问题

    启动tomcat时,有时会出现8080端口占用的问题. 解决方法: 终端:ps -e | grep tomcat 会看到下边的结果 途中标记的是进程号,kill掉即可. kill -9 9734(97 ...

  4. MySQL-第十四篇事务管理

    1.什么是事务 事务是由一步或者几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行. 2.事务具备的4个特性: 1>原子性(Atomicity):事务是应用中最小的执 ...

  5. 如何在Web工程中实现任务计划调度

    转载自: http://www.oschina.net/question/146385_37793?sort=time 下面就Servlet侦听器结合Java定时器来讲述整个实现过程.要运用Servl ...

  6. java 多态 (知识点不完整)

    1.多态的条件 1.要有继承 2.方法重写 3.父类引用指向子类对象 多态中成员访问 1.   访问成员变量:  编译看左边,运行也看左边 f.num = 10 因为这里是父类的,看是父类Father ...

  7. OSI模型——传输层

    OSI模型——传输层 运输层 运输层概述 运输层提供应用层端到端通信服务,通俗的讲,两个主机通讯,也就是应用层上的进程之间的通信,也就是转换为进程和进程之间的通信了,我们之前学到网络层,IP协议能将分 ...

  8. 什么是 Python?

     Python 是一种编程语言,它有对象.模块.线程.异常处理和自动内存管理,可以加入其他语言的对比.  Python 是一种解释型语言,Python 在代码运行之前不需要解释.  Python 是动 ...

  9. python 导入re模块语法及规则

    正则表达式是功能比较强大的模块,应用在很多地方,抓网页,数据分析,数据验证等,下面讲述python 导入re模块语法及规则. 1,re模块语法 re.match 从头开始匹配 re.search 匹配 ...

  10. 最长回文子序列/最长回文子串(DP,马拉车)

    字符子串和字符子序列的区别 字符字串指的是字符串中连续的n个字符:如palindrome中,pa,alind,drome等都属于它的字串 而字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符: ...