Python 是一种独特的编程语言,它语法简洁、易于学习,功能却非常强大。Python 有很多进阶用法,今天我们来聊聊装饰器 @classmethod
和 @staticmethod
。可能在我们的很多项目中可能用不到,但在了解了这些之后可以使我们的开发变得更加高效。如何创建Python静态方法并不明显,这就是这两个装饰器的用武之地。
本文会讲讲每个装饰器的作用以及它们的不同之处,并会给一些示例。
@classmethod 装饰器
有了这个装饰器,我们就可以创建在函数调用中传递实际类对象的类方法,就像 self
传递给类中任何其他普通实例方法一样。
在对象的实例方法中,self
参数是类实例对象本身,我们可以用它来对实例数据进行一些操作。@classmethod
方法也有一个强制性的第一个参数,它表示的是未实例化的类本身,而非类的实例。
我们来看一个典型的类方法的用法:
class Student(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
scott = Student('Scott', 'Robinson')
如果我们用 @classmethod 的话就可以这样表示:
class Student(object):
@classmethod
def from_string(cls, name_str):
first_name, last_name = map(str, name_str.split(' '))
student = cls(first_name, last_name)
return student
scott = Student.from_string('Scott Robinson')
这样就很好地遵循了静态工厂模式,将解析逻辑封装在方法本身内部。如此我们的代码就有了更丰富的表达能力,它便可以处理多种格式来源的数据统一解析成我们的的对象了:
class Student(object):
@classmethod
def from_string(cls, name_str):
first_name, last_name = map(str, name_str.split(' '))
student = cls(first_name, last_name)
return student
@classmethod
def from_json(cls, json_obj):
# parse json...
return student
@classmethod
def from_pickle(cls, pickle_file):
# load pickle file...
return student
当在子类中实现这些方法时,装饰器将会变得更加有用。
@staticmethod 装饰器
@staticmethod
装饰类似于@classmethod
,它能够从一个非实例类对象被调用,但是没有传递 cls
参数。我们来看一个例子:
class Student(object):
@staticmethod
def is_full_name(name_str):
names = name_str.split(' ')
return len(names) > 1
Student.is_full_name('Scott Robinson') # True
Student.is_full_name('Scott') # False
由于没有 self
传递任何对象,这意味着此装饰器方法无法访问任何实例数据,并且也无法在实例化对象上调用此方法。
这些类型的方法通常不是为了创建/实例化对象,它们是为了处理一些与类本身有关的逻辑。
@classmethod vs @staticmethod
这些装饰器之间最明显的事情是它们能够在类中创建静态方法,可以在未实例化的类对象上调用这些类型的方法,就像在 Java 中使用关键字 static
的类一样。
这两个方法装饰器之间实际上只有一个区别。在上文对这两种装饰器的介绍中我们可以发现,@classmethod
方法有一个cls
参数会传递到他们的方法中,而@staticmethod
方法没有。
这个cls
参数是我们讨论过的类对象,它允许@classmethod
方法方便地实例化类。而方法中缺少cls
参数的@staticmethod
是真正传统意义上的静态方法。它们的主要目的是处理包含与类有关的逻辑,并且该逻辑类实例数据无关。
一个完整的例子
现在让我们看另一个例子,在同一个类中同时使用这两种类型:
# static.py
class ClassGrades:
def __init__(self, grades):
self.grades = grades
@classmethod
def from_csv(cls, grade_csv_str):
grades = list(map(int, grade_csv_str.split(', ')))
cls.validate(grades)
return cls(grades)
@staticmethod
def validate(grades):
for g in grades:
if g < 0 or g > 100:
raise Exception()
try:
# Try out some valid grades
class_grades_valid = ClassGrades.from_csv('90, 80, 85, 94, 70')
print('Got grades:', class_grades_valid.grades)
# Should fail with invalid grades
class_grades_invalid = ClassGrades.from_csv('92, -15, 99, 101, 77, 65, 100')
print(class_grades_invalid.grades)
except:
print('Invalid!')
执行
$ python static.py
Got grades: [90, 80, 85, 94, 70]
Invalid!
可以好好体会下两者的用法哦。
注意:本文归作者所有,未经作者允许,不得转载