1.命名空间
关于python中的命名空间,可以先看官方文档里的一个说明
A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。
1.namespace(命名空间)是从名称到对象的映射。 2.大部分的namespace目前都是通过python中的字典实现的。
命令空间为项目中提供了一种避免名字冲突的方法。不同命名空间之前是相互独立的,因此在同一个命名空间中不能重名,但是不同命名空间可以名字一样。
跟这情况很像的就是文件系统:如果把一个文件夹类比为命名空间,那么同一个文件夹中不能有相当名字的文件或者文件夹,但是不同文件夹中可以有相同名字的文件或者文件夹。
2.类命名空间与类对象命名空间
在python里,类与对象都有自己的命名空间,可以看如下例子。
class TestClass(object):
class_value = 1
def __init__(self, value):
self.value = value
t = TestClass(10)
print(TestClass.__dict__)
print(t.__dict__)
{'__module__': '__main__', 'class_value': 1, '__init__': <function TestClass.__init__ at 0x100955290>, '__dict__': <attribute '__dict__' of 'TestClass' objects>, '__weakref__': <attribute '__weakref__' of 'TestClass' objects>, '__doc__': None}
{'value': 10}
可以通过__dict__访问类或者对象的命名空间。我们通过"."去访问对象的属性或者方法,其实就是去对象的命名空间找对应的名字。
当通过名字访问对象属性的时候,访问属性如下: 1.先从对象的命名空间寻找对应属性,如果找到对应属性,直接返回结果。 2.如果在对象的命名空间没有找到,则从类的命名空间继续寻找,找到了返回。 3.还找不到,抛异常。
class TestClass(object):
class_value = 1
def __init__(self, value):
self.value = value
t = TestClass(10)
print(t.value)
print(t.class_value)
print(TestClass.class_value)
输出结果
10
1
1
通过上面的例子不难看出,类变量即可以被实例对象访问,也可以被类名直接访问。
3.类变量用途
可以用来定义常量
class Round(object):
pi = 3.14
def __init__(self, r):
self.r = r
def calc_area(self):
return Round.pi * self.r * self.r
round = Round(2)
print(round.pi)
print(round.calc_area())
输出结果为
3.14
12.56
可以用来充当计数器
class Round(object):
cur_count = 0
max_count = 5
def __init__(self):
self.data = []
def add_item(self, num):
if len(self.data) >= Round.max_count:
print("the data list is full!")
else:
self.data.append(num)
Round.cur_count += 1
round = Round()
round.add_item(1)
round.add_item(2)
round.add_item(3)
print(round.cur_count)
round.add_item(4)
round.add_item(5)
print(round.cur_count)
round.add_item(6)
print(round.cur_count)
3
5
the data list is full!
5
还可以记录所有类对象
class Round(object):
rs = []
def __init__(self, r):
self.r = r
Round.rs.append(r)
r1 = Round(1)
r2 = Round(2)
r3 = Round(3)
print(Round.rs)
[1, 2, 3]
4.通过类对象无法给类变量赋值
class MyClass(object):
class_value = 1
def __init__(self, value):
self.value = value
print("MyClass.class_value is: ", MyClass.class_value)
obj = MyClass(10)
print(obj.__dict__)
print("obj.class_value is: ", obj.class_value)
obj.class_value = 2
print(obj.__dict__)
print("obj.class_value is: ", obj.class_value)
print("MyClass.class_value is: ", MyClass.class_value)
print(MyClass.__dict__)
MyClass.class_value is: 1
{'value': 10}
obj.class_value is: 1
{'value': 10, 'class_value': 2}
obj.class_value is: 2
MyClass.class_value is: 1
{'__module__': '__main__', 'class_value': 1, '__init__': <function MyClass.__init__ at 0x10114f290>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
通过上面例子不难看出,obj.class_value = 2 这句并不能直接改变类属性的值,而是给obj自己的命名空间中新添加了一个class_value变量,类空间中的class_value值仍为1。 总结起来就是:如果通过对象来给类变量赋值,将只会覆盖那个对象中的值,类变量的值并未发生改变。
5.可变属性
如果类命名空间中的属性是可变的,那么如果类实例改变了属性,类属性也会发生改变。
class MyClass(object):
data = []
def __init__(self, value):
self.value = value
m1 = MyClass(111)
m2 = MyClass(222)
m1.data.append(1)
print(m1.data)
print(MyClass.data)
m2.data.append(2)
print(m2.data)
print(m1.data)
print(MyClass.data)
[1]
[1]
[1, 2]
[1, 2]
[1, 2]
所以如果类属性是可变类型,一定要引起高度注意。
6.对象成员变量的set/get方法实现
java代码里头,通常通过get/set的方式操作对象属性,python里面也可以通过property的方式实现。
class DefineError(object):
def __init__(self, errors):
self._errors = errors
@property
def errors(self):
if self._errors is None:
print("errors is None")
return self._errors
@errors.setter
def errors(self, value):
self._errors = value
也可以如下写法
class CustomizeError(object):
def __init__(self, errors):
self._errors = errors
def _get_errors(self):
return self._errors
def _set_errors(self, value):
self._errors = value
errors = property(fget=_get_errors, fset=_set_errors)
需要注意的一点是,属性的方法名不能与实例变量名相同。
class DefineError(object):
def __init__(self, errors):
self.errors = errors
@property
def errors(self):
if self.errors is None:
print("errors is None")
return self.errors
@errors.setter
def errors(self, value):
self.errors = value
比如上面的代码,会报错。
......
[Previous line repeated 993 more times]
RecursionError: maximum recursion depth exceeded
因为在调用obj.errors的时候,首先会转化为方法调用,会执行return self.errors这一行。这个时候会被视为属性调用,有转化为方法调用,最后就是一个死循环,然后抛出RecursionError的异常。
7.下划线的使用
单下划线:
_XX: 约定成员私有
XX_: 与python关键词区分
双下划线:
__XX: 避免子类重写
__XX__: 内置特殊成员
|