python命名空间,类成员,对象成员

Posted bitcarmanlee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python命名空间,类成员,对象成员相关的知识,希望对你有一定的参考价值。

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__: 内置特殊成员


以上是关于python命名空间,类成员,对象成员的主要内容,如果未能解决你的问题,请参考以下文章

面向对象:类的成员---只是特定(类的)命名空间的全局变量(函数)而已

C++|详解类成员指针:数据成员指针和成员函数指针及应用场合

js单例——如何避免通过命名空间访问类成员

在 C++ 中使用类的命名空间定义类的成员

访问修饰符,命名空间

命名空间