zoukankan      html  css  js  c++  java
  • Python 学习笔记 10.类(Class)

    转载:http://www.xwy2.com/article.asp?id=119

     

    定义


    Python 的 Class 比较特别,和我们习惯的静态语言类型定义有很大区别。

    1. 使用一个名为 __init__ 的方法来完成初始化。
    2. 使用一个名为 __del__ 的方法来完成类似析购操作。
    3. 所有的实例方法都拥有一个 self 参数来传递当前实例,类似于 this。
    4. 可以使用 __class__ 来访问类型成员。

    Code
    >>>>>> class MyClass:
    def __init__(self):
    print "initialize."
    def Foo(self):
    print id(self)


    >>>>>> a = MyClass()
    initialize.
    >>>>>> a.Foo()
    14412576
    >>>>>> id(a)
    14412576
    
    

    Class 有一些特殊的属性,便于我们获得一些额外的信息。

     

    Code
    >>>>>> class MyClass(object):
    """This is MyClass's Docoment"""
    def __init__(self):
    self.i
    = 1234


    >>>>>> MyClass.__doc__ # 类型帮助信息
    "This is MyClass's Docoment""This is MyClass's Docoment"
    >>>>>> MyClass.__name__ # 类型名称
    'MyClass'
    >>>>>> MyClass.__module__ # 类型所在模块
    '__main__'
    >>>>>> MyClass.__bases__ # 类型所继承的基类(Python 支持多继承)
    (<type 'object'>,)
    >>>>>> MyClass.__dict__ # 类型字典,存储所有类型成员信息
    <dictproxy object at 0x00DC1AD0>
    >>>>>> #以下是实例拥有的属性
    >>>>>> MyClass().__class__ # 实例的类型
    <class '__main__.MyClass'>
    >>>>>> MyClass().__module__ # 实例类型所在模块
    '__main__'
    >>>>>> MyClass().__dict__ # 对象字典,存储所有实例成员信息
    {'i': 1234}
    >>>>>>
    
    

     

    继承


    Python 支持多继承,但有几点需要注意:

    1. 基类 __init__ / __del__ 需显示调用。
    2. 继承方法的调用和基类声明顺序有关。

    Code
    >>>>>> class Base1:
    def __init__(self):
    print "Base1"
    def test(self):
    print "Base1 test"


    >>>>>> class Base2:
    def __init__(self):
    print "Base2"
    def test(self):
    print "Base2 test"


    >>>>>> class MyClass(Base2,Base1):
    def __init__(self):
    Base1.
    __init__(self)
    Base2.
    __init__(self)
    print "MyClass"


    >>>>>> a = MyClass()
    Base1
    Base2
    MyClass
    >>>>>> a.test()
    Base2 test
    >>>>>> # 下面把 Base1 放在前面
    >>>>>> class MyClass(Base1,Base2):
    def __init__(self):
    Base1.
    __init__(self)
    Base2.
    __init__(self)
    print "MyClass"


    >>>>>> a = MyClass()
    Base1
    Base2
    MyClass
    >>>>>> a.test()
    Base1 test
    >>>>>>
    
    

    成员


    Python Class 同样包含类型和实例两种成员。

    Code
    >>>>>> class Class1:
    i
    = 123 # 类成员
    def __init__(self):
    self.i
    = 100 # 实例成员


    >>>>>> print Class1.i
    123
    >>>>>> print Class1().i
    100
    >>>>>>
    
    

    有几个很 "特殊" 的 "规则" 需要注意。

    (1) 我们可以通过实例引用访问类型成员。因此下面的例子中 self.i 实际指向 Class1.i,直到我们为实例新增了一个成员 i。

     

    Code
    >>>>>> class Class1:
    i
    = 123
    def __init__(self):
    print self.i
    print hex(id(self.i))


    >>>>>> hex(id(Class1.i)) # 显示 Class1.i 的地址
    '0xab5860'
    >>>>>> a = Class1() # 创建 Class1 实例,我们会发现 self.i 实际指向 Class1.i
    123
    0xab5860
    >>>>>> Class1.__dict__ # 显示 Class1 成员
    {'i': 123, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x012911B0>}
    >>>>>> a.__dict__ # 显示实例成员
    {}
    >>>>>> a.i = 100 # 为实例新增加一个成员i
    >>>>>> hex(id(a.i)) # 显示新成员i的地址
    '0xab5974'
    >>>>>> a.__dict__ # 显示实例成员
    {'i': 100}
    >>>>>>
    
    

    (2) 调用类型内部方法,需要省略 self 参数。

     

    Code
    >>>>>> class Class1:
    def __init__(self):
    self.
    __test("Hello Python")
    def __test(self, s):
    print s


    >>>>>> Class1()
    Hello Python
    <__main__.Class1 instance at 0x00DC3800>
    >>>>>>
    
    

    我们可以在成员名称前添加 "__" 使其成为私有成员。

     

    Code
    >>>>>> class Class1:
    __i = 123
    def __init__(self):
    self.
    __x = 0
    def __test(self):
    print id(self)


    >>>>>> Class1.i

    Traceback (most recent call last):
    File
    "<pyshell#203>", line 1, in <module>
    Class1.i
    AttributeError:
    class Class1 has no attribute 'i'
    >>>>>> Class1().__x

    Traceback (most recent call last):
    File
    "<pyshell#204>", line 1, in <module>
    Class1().
    __x
    AttributeError: Class1 instance has no attribute
    '__x'
    >>>>>> Class1().__test()

    Traceback (most recent call last):
    File
    "<pyshell#205>", line 1, in <module>
    Class1().
    __test()
    AttributeError: Class1 instance has no attribute
    '__test'
    >>>>>>
    
    

    事实上这只是一种规则,并不是编译器上的限制。我们依然可以用特殊的语法来访问私有成员。

     

    Code
    >>>>>> Class1._Class1__i
    123
    >>>>>> a = Class1()
    >>>>>> a._Class1__x
    0
    >>>>>> a._Class1__test()
    14432256
    >>>>>>
    
    

    除了静态(类型)字段,我们还可以定义静态方法。

     

    Code
    >>>>>> class Class1:
    @staticmethod
    def test():
    print "In Static method"


    >>>>>> Class1.test()
    In Static method
    >>>>>>
    
    

    从设计的角度,或许更希望用属性(property)来代替字段(field)。

     

    Code
    >>>>>> class Class1:
    def __init__(self):
    self.
    __i = 1234
    def getI(self): return self.__i
    def setI(self, value): self.__i = value
    def delI(self): del self.__i
    I
    = property(getI, setI, delI, "Property I")


    >>>>>> a = Class1()
    >>>>>> a.I
    1234
    >>>>>> a.I = 1000
    >>>>>> a.I
    1000
    >>>>>>
    
    

    如果只是 readonly property,还可以用另外一种方式。

     

    Code
    >>>>>> class Class1:
    def __init__(self):
    self.
    __i = 1234
    @property
    def I(self):
    return self.__i


    >>>>>> a = Class1()
    >>>>>> a.I
    1234
    >>>>>>
    
    

    用 __getitem__ 和 __setitem__ 可以实现 C# 索引器的功能。

     

    Code
    >>>>>> class Class1:
    def __init__(self):
    self.
    __x = ["a", "b", "c"]
    def __getitem__(self, key):
    return self.__x[key]
    def __setitem__(self, key, value):
    self.
    __x[key] = value


    >>>>>> a = Class1()
    >>>>>> a[1]
    'b'
    >>>>>> a[1] = "xxx"
    >>>>>> a[1]
    'xxx'
    >>>>>>
    
    

    重载


    Python 支持一些特殊方法和运算符重载。

    Code
    >>>>>> class Class1:
    def __init__(self):
    self.i
    = 0
    def __str__(self):
    return "id=%i"%(id(self))
    def __add__(self, other):
    return self.i + other.i


    >>>>>> a = Class1()
    >>>>>> a.i = 10
    >>>>>> str(a)
    'id=19481664'
    >>>>>> b = Class1()
    >>>>>> b.i = 20
    >>>>>> a + b
    30
    >>>>>>
    
    

    通过重载 "__eq__",我们可以改变 "==" 运算符的行为。

     

    Code
    >>>>>> class Class1:
    pass

    >>>>>> a = Class1()
    >>>>>> b = Class1()
    >>>>>> a ==b
    False
    >>>>>> # 实现操作符__eq__重载
    >>>>>> class Class1:
    def __eq__(self, other):
    return True


    >>>>>> a = Class1()
    >>>>>> b = Class1()
    >>>>>> a == b
    True
    >>>>>>
    
    

    Open Class



    这是个有争议的话题。在 Python 中,我们随时可以给类型或对象添加新的成员。

    1. 添加字段

    Code
    >>>>>> class Class1:
    pass

    >>>>>> a = Class1()
    >>>>>> a.x = 10
    >>>>>> a.x
    10
    >>>>>> dir(a)
    [
    '__doc__', '__module__', 'x']
    >>>>>> b = Class1()
    >>>>>> dir(b)
    [
    '__doc__', '__module__']
    >>>>>> del a.x
    >>>>>> dir(a)
    [
    '__doc__', '__module__']
    >>>>>> dir(Class1)
    [
    '__doc__', '__module__']
    >>>>>> Class1.x = 100
    >>>>>> Class1.x
    100
    >>>>>> dir(Class1)
    [
    '__doc__', '__module__', 'x']
    >>>>>>
    
    

    2. 添加方法

    Code
    >>>>>> class Class1:
    pass

    >>>>>> def test():
    print "test"


    >>>>>> def hello(self):
    print "hello ",id(self)


    >>>>>> Class1.test = test
    >>>>>> a = Class1()
    >>>>>> dir(a)
    [
    '__doc__', '__module__', 'test']
    >>>>>> b = Class1()
    >>>>>> a.hello = hello
    >>>>>> dir(a)
    [
    '__doc__', '__module__', 'hello', 'test']
    >>>>>> a.hello()

    Traceback (most recent call last):
    File
    "<pyshell#340>", line 1, in <module>
    a.hello()
    TypeError: hello() takes exactly
    1 argument (0 given)
    >>>>>> a.hello(a)
    hello
    19474632
    >>>>>> a.hello(b)
    hello
    19481584
    >>>>>> dir(b)
    [
    '__doc__', '__module__', 'test']
    >>>>>> b.test()

    Traceback (most recent call last):
    File
    "<pyshell#344>", line 1, in <module>
    b.test()
    TypeError: test() takes no arguments (
    1 given)
    >>>>>>
    
    

    3. 改变现有方法

    Code
    >>>>>> class Class1:
    def test(self):
    print "a"


    >>>>>> def test(self):
    print "b"


    >>>>>> Class1.test = test
    >>>>>> Class1().test()
    b
    >>>>>>
    
    

    另外,有几个内建函数方便我们在运行期进行操作。

    Code
    >>>>>> hasattr(a, "x")
    False
    >>>>>> a.x = 10
    >>>>>> getattr(a, "x")
    10
    >>>>>> setattr(a, "b", 1234)
    >>>>>> a.b
    1234
    >>>>>>
    
    

    Python Open Class 是如何实现的呢?我们看一下下面的代码。

    Code
    >>> class Class1:
    pass

    >>> a = Class1()
    >>> a.__dict__
    {}
    >>> a.x = 123
    >>> a.__dict__
    {
    'x': 123}
    >>> a.test = lambda i: i*10
    >>> a.test(1)
    10
    >>> a.__dict__
    {
    'test': <function <lambda> at 0x012912F0>, 'x': 123}
    >>>
    
    

    原来,Python Class 对象或类型通过内置成员 __dict__ 来存储成员信息。

    我们还可以通过重载 __getattr__ 和 __setattr__ 来拦截对成员的访问,需要注意的是 __getattr__ 只有在访问不存在的成员时才会被调用。

    Code
    >>>>>> class Class1:
    def __getattr__(self, name):
    print "__getattr__", name
    return None
    def __setattr__(self, name, value):
    print "__setattr__", name, value
    self.
    __dict__[name] = value


    >>>>>> a = Class1()
    >>>>>> a.x
    __getattr__ x
    >>>>>> a.x = 123
    __setattr__ x 123
    >>>>>> a.x
    123
    >>>>>>
    
    

    如果类型继承自 object,我们可以使用 __getattribute__ 来拦截所有(包括不存在的成员)的获取操作。
    注意在 __getattribute__ 中不要使用 "return self.__dict__[name]" 来返回结果,因为在访问 "self.__dict__" 时同样会被 __getattribute__ 拦截,从而造成无限递归形成死循环。

    Code
    >>>>>> class Class1(object):
    def __getattribute__(self, name):
    print "__getattribute", name
    return object.__getattribute__(self, name)


    >>>>>> a = Class1()
    >>>>>> a.x
    __getattribute x

    Traceback (most recent call last):
    File
    "<pyshell#396>", line 1, in <module>
    a.x
    File
    "<pyshell#394>", line 4, in __getattribute__
    return object.__getattribute__(self, name)
    AttributeError:
    'Class1' object has no attribute 'x'
    >>>>>> a.x = 123
    >>>>>> a.x
    __getattribute x
    123
    >>>>>>
    
    

  • 相关阅读:
    Java-----Excel转HTML
    UI_拖动View
    查找——图文翔解Treap(树堆)
    android 三级菜单 BaseExpandableListAdapter
    New Relic——手机应用app开发达人的福利立即就到啦!
    安卓 下载多线程带进度条
    大数据
    Android学习路线(六)为Android应用加入ActionBar
    JavaScript日期对象使用总结
    经典二叉树
  • 原文地址:https://www.cnblogs.com/sislcb/p/1285514.html
Copyright © 2011-2022 走看看