hasattr
, getattr
, setattr
and delattr
:属性助手我们知道,在字典中,可以通过键访问键值对的值:
>>> dictionary = {'property': 42}
>>> dictionary['property']
42
而在对象上,它是通过“ .
”操作符访问到属性的值:
>>> class C:
... prop = 42
...
>>> C.prop
42
你甚至可以设置和删除对象的属性:
>>> C.prop = 84
>>> C.prop
84
>>> del C.prop
将字典的键值对和对象及其属性比较,两者具有很高的相似性,但字典要灵活得多,例如,可以检查字典中是否存在一个键(对应于对象的属性):
>>> d = {}
>>> 'prop' in d
False
>>> d['prop'] = 'exists'
>>> 'prop' in d
True
对于实例的属性而言,类似的操作可以通过 try ... except
语句完成:
>>> class X:
... pass
...
>>> x = X()
>>> try:
... print(x.prop)
>>> except AttributeError:
... print("prop doesn't exist.")
prop doesn't exist.
但是,这并不是常用的,更提倡使用 hasattr
函数判断对象是否含有某个属性。
>>> class X:
... pass
...
>>> x = X()
>>> hasattr(x, 'prop')
False
>>> x.prop = 'exists'
>>> hasattr(x, 'prop')
True
下面的操作,很显然不成立:
>>> class X:
... value = 42
...
>>> x = X()
>>> attr_name = 'value'
>>> x.attr_name
AttributeError: 'X' object has no attribute 'attr_name'
但是,如果想让它成为现实,可以这么做:
>>> class X:
... value = 42
...
>>> x = X()
>>> getattr(x, 'value')
42
>>> attr_name = 'value'
>>> getattr(x, attr_name)
42 # It works!
这里使用了 getattr
函数,它能接受一个字符串为属性,并设置属性值。
setattr
和 delattr
也都有类似的功能:它们接受字符串的属性名称,并相应地设置或删除属性值。
>>> class X:
... value = 42
...
>>> x = X()
>>> setattr(x, 'value', 84)
>>> x.value
84
>>> delattr(x, 'value') # deletes the attribute completety
>>> hasattr(x, 'value')
False # `value` no longer exists on the object.
下面试着用其中的一个函数来构建一些有意义的东西:
class api:
"""A dummy API."""
def send(item):
print(f'Uploaded {item!r}!')
def upload_data(item):
"""Uploads the provided value to our database."""
if hasattr(item, 'get_value'):
data = item.get_value()
api.send(data)
else:
api.send(item)
这里的 upload_data
函数检查实参是否有 get_value
方法,如果有,则通过此方法读取数据。符合此类要求的对象(作为实参)是:
>>> import json
>>> class DataCollector:
... def __init__(self):
... self.items = []
... def add_item(self, item):
... self.items.append(item)
... def get_value(self):
... return json.dumps(self.items)
...
>>> upload_data('some text')
Uploaded 'some text'!
>>> collector = DataCollector()
>>> collector.add_item(42)
>>> collector.add_item(1000)
>>> upload_data(collector)
Uploaded '[42, 1000]'!
super
:用于继承super
是 Python 引用父类的方式,例如,为了使用父类的方法而使用 super
。
以下面的类为例,它的作用是实现两项相加。
class Sum:
def __init__(self, x, y):
self.x = x
self.y = y
def perform(self):
return self.x + self.y
这个类的应用非常简单:
>>> s = Sum(2, 3)
>>> s.perform()
5
现在又创建了类 DoubleSum
,并且这个类继承了 Sum
类。在 DoubleSum
类中,有与父类相同的方法 perform
,但是它的返回值是两项和的 2 倍。这时候如果不断算写重复代码,而是依然要首先调用父类中的 perform
方法计算两项和,就可以用 super
方法实现:
class DoubleSum(Sum):
def perform(self):
parent_sum = super().perform()
return 2 * parent_sum
这样做,不需要重复已经定义过的东西,比如 __init__
方法,也不用重复写求和逻辑,只需要在继承父类的基础上调用父类方法即可。
>>> d = DoubleSum(3, 5)
>>> d.perform()
16
super
函数也不只用在类中,其他地方也可以使用,比如:
>>> super(int)
<super: <class 'int'>, NULL>
>>> super(int, int)
<super: <class 'int'>, <int object>>
>>> super(int, bool)
<super: <class 'int'>, <bool object>>
但说实话,我不明白这些有什么用。如果你知道,请在评论中告诉我,感谢。
property
, classmethod
and staticmethod
:方法的装饰器这三个是对类中方法的三个装饰器:
property
:
当你想要在类中对属性通过 getter
或 setter
进行读写时,就要使用装饰器 @property
。在此情形下,可以通过 getter
或 setter
对读写过程给予干涉。
通过 property
装饰器,将属性转换为一组方法来实现的:一个方法在访问属性时运行,另一个方法尝试更改属性值时运行。
看一个例子,我们试图确保一个学生的 marks
属性总是被设置为正数,因为学习成绩不能是负数:
class Student:
def __init__(self):
self._marks = 0
@property
def marks(self):
return self._marks
@marks.setter
def marks(self, new_value):
# Doing validation
if new_value < 0:
raise ValueError('marks cannot be negative')
# before actually setting the value.
self._marks = new_value
运行这段代码:
>>> student = Student()
>>> student.marks
0
>>> student.marks = 85
>>> student.marks
85
>>> student.marks = -10
ValueError: marks cannot be negative
classmethod
:
装饰器 @classmethod
用在方法上,使其成为类方法。这样一来,它就获得对类对象的引用,而不是对实例 (self
)的引用。
比如,在类里面创建一个返回类名称的方法:
>>> class C:
... @classmethod
... def class_name(cls):
... return cls.__name__
...
>>> x = C()
>>> x.class_name
'C'
staticmethod
:
装饰器 @staticmethod
用于将方法转换为静态方法:相当于位于类中的函数,独立于任何类或对象属性。使用 @staticmethod
后的方法就不必在参数列表中的第一个位置使用 self
参数。
We could make one that does some data validation for example:
下面的应用中是利用 @staticmethod
进行数据验证:
class API:
@staticmethod
def is_valid_title(title_text):
"""Checks whether the string can be used as a blog title."""
return title_text.istitle() and len(title_text) < 60
这些内置函数是使用一个非常高级的主题 descriptors(描述符)创建的。坦率地说,描述符是一个非常高级的话题,如果在这里试图涵盖它,没有任何用处,因为它只是和已告知的内容有所关联。
list
, tuple
, dict
, set
and frozenset
:容器Python 中的“容器”指的是一类数据结构,在这类数据结构中可以保存任意数量的成员。
Python 有5种基本容器类型:
list
:有序的索引容器。每个元素都有一个特定的索引。列表是可变的,即:可以在任何时候添加或删除成员。
>>> my_list = [10, 20, 30] # Creates a list with 3 items
>>> my_list[0] # Indexes start with zero
10
>>> my_list[1] # Indexes increase one by one
20
>>> my_list.append(40) # Mutable: can add values
>>> my_list
[10, 20, 30, 40]
>>> my_list[0] = 50 # Can also reassign indexes
>>> my_list
[50, 20, 30, 40]
tuple
:像列表一样有序和,但有一个关键区别: 它们是不可变的,这意味着一旦创建了元组对象,就不能添加或删除其中的成员。
>>> some_tuple = (1, 2, 3)
>>> some_tuple[0] # Indexable
1
>>> some_tuple.append(4) # But NOT mutable
AttributeError: ...
>>> some_tuple[0] = 5 # Cannot reassign an index as well
TypeError: ...
dict
:以键值对为成员。
>>> flower_colors = {'roses': 'red', 'violets': 'blue'}
>>> flower_colors['violets'] # Use keys to access value
'blue'
>>> flower_colors['violets'] = 'purple' # Mutable
>>> flower_colors
{'roses': 'red', 'violets': 'purple'}
>>> flower_colors['daffodil'] = 'yellow' # Can also add new values
>>> flower_colors
{'roses': 'red', 'violets': 'purple', 'daffodil': 'yellow'}
set
:是由无序的、唯一的成员组成。
>>> forest = ['cedar', 'bamboo', 'cedar', 'cedar', 'cedar', 'oak', 'bamboo']
>>> tree_types = set(forest)
>>> tree_types
{'bamboo', 'oak', 'cedar'} # Only unique items
>>> 'oak' in tree_types
True
>>> tree_types.remove('oak') # Sets are also mutable
>>> tree_types
{'bamboo', 'cedar'}
frozenset
与集合相同,但它是不可变的。
>>> forest = ['cedar', 'bamboo', 'cedar', 'cedar', 'cedar', 'oak', 'bamboo']
>>> tree_types = frozenset(forest)
>>> tree_types
frozenset({'bamboo', 'oak', 'cedar'})
>>> 'cedar' in tree_types
True
>>> tree_types.add('mahogany') # CANNOT modify
AttributeError: ...
内置的 list
、tuple
和 dict
也可以用来创建这些数据结构的空实例:
>>> x = list()
>>> x
[]
>>> y = dict()
>>> y
{}