• Python类中的特殊方法


    类中的特殊方法

    一般说来,特殊的方法都被用来模仿某个行为。例如,如果你想要为你的类使用x[key]这样的索引操作(就像列表和元组一样),那么你只需要实现__getitem__()方法就可以了。想一下,Python就是对list类这样做的!

    下面这个表中列出了一些有用的特殊方法。如果你想要知道所有的特殊方法,你可以在《Python参考手册》中找到一个庞大的列表。

    名称                     说明
    ---------------------------------------------------------
    __init__(self,...)      这个方法在新建对象恰好要被返回使用之前被调用。
    __del__(self)           恰好在对象要被删除之前调用。
    __str__(self)           在我们对对象使用print语句或是使用str()的时候调用。
    __lt__(self,other)      当使用 小于 运算符(<)的时候调用。类似地,对于所有的运算符(+>等等)都有特殊的方法。
    __getitem__(self,key)   使用x[key]索引操作符的时候调用。
    __len__(self)           对序列对象使用内建的len()函数的时候调用。
    __repr__(s)             repr() and `...` conversions
    __cmp__(s, o)           Compares s to o and returns <0, 0, or >0. 
                            Implements 
    ><== etc...
    __hash__(s)             Compute a 32 bit hash code; hash() and dictionary ops
    __nonzero__(s)          Returns 0 or 1 for truth value testing
    __getattr__(s, name)    called when attr lookup doesn't find 
    __setattr__(s, name, val) called when setting an attr
                            (inside, don
    't use "self.name = value"
                            use "self.__dict__[name] = val")
    __delattr__(s, name)    called to delete attr <name>
    __call__(self, *args)   called when an instance is called as function.

    exec和eval语句

    exec语句用来执行储存在字符串或文件中的Python语句。例如,我们可以在运行时生成一个包含Python代码的字符串,然后使用exec语句执行这些语句。
    下面是一个简单的例子。

    >>> exec 'print "Hello World"'
    Hello World

    eval语句用来计算存储在字符串中的有效Python表达式。下面是一个简单的例子。

    >>> eval('2*3')
    6 

    assert语句

    assert 语句用来声明某个条件是真的。例如,如果你非常确信某个你使用的列表中至少有一个元素,而你想要检验这一点,并且在它非真的时候引发一个错误,那么 assert语句是应用在这种情形下的理想语句。当assert语句失败的时候,会引发一个AssertionError。

    >>> mylist = ['item']
    >>> assert len(mylist) >= 1
    >>> mylist.pop()
    'item'
    >>> assert len(mylist) >= 1
    Traceback (most recent call last):
      File 
    "", line 1in ?
    AssertionError repr函数
    repr函数用来取得对象的规范字符串表示。反引号(也称转换符)可以完成相同的功能。注意,在大多数时候有eval(repr(object)) == object。
    >>> i = []
    >>> i.append('item')
    >>> `i`
    "['item']"
    >>> repr(i)
    "['item']"

    基本上,repr函数和反引号用来获取对象的可打印的表示形式。你可以通过定义类的__repr__方法来控制你的对象在被repr函数调用的时候返回的内容。

    类和实例变量

    有两种类型的域 —— 类的变量和对象的变量,它们根据是类还是对象拥有这个变量而区分。

    类的变量 由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,所以当某个对象对类的变量做了改动的时候,这个改动会反映到所有其他的实例上。

    对象的变量 由类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是是互不相关的。通过一个例子会使这个易于理解。

    #!/usr/bin/python
    #
     Filename: objvar.py

    class Person:
        
    '''Represents a person.'''
        population 
    = 0

        
    def __init__(self, name):
            
    '''Initializes the person's data.'''
            self.name 
    = name
            
    print '(Initializing %s)' % self.name

            
    # When this person is created, he/she
            # adds to the population
            Person.population += 1

        
    def __del__(self):
            
    '''I am dying.'''
            
    print '%s says bye.' % self.name

            Person.population 
    -= 1

            
    if Person.population == 0:
                
    print 'I am the last one.'
            
    else:
                
    print 'There are still %d people left.' % Person.population

        
    def sayHi(self):
            
    '''Greeting by the person.

            Really, that's all it does.
    '''
            
    print 'Hi, my name is %s.' % self.name

        
    def howMany(self):
            
    '''Prints the current population.'''
            
    if Person.population == 1:
                
    print 'I am the only person here.'
            
    else:
                
    print 'We have %d persons here.' % Person.population

    swaroop 
    = Person('Swaroop')
    swaroop.sayHi()
    swaroop.howMany()

    kalam 
    = Person('Abdul Kalam')
    kalam.sayHi()
    kalam.howMany()

    swaroop.sayHi()
    swaroop.howMany()输出

    ___FCKpd___5nbsp;python objvar.py
    (Initializing Swaroop)
    Hi, my name 
    is Swaroop.
    I am the only person here.
    (Initializing Abdul Kalam)
    Hi, my name 
    is Abdul Kalam.
    We have 
    2 persons here.
    Hi, my name 
    is Swaroop.
    We have 
    2 persons here.
    Abdul Kalam says bye.
    There are still 
    1 people left.
    Swaroop says bye.
    I am the last one.

    这是一个很长的例子,但是它有助于说明类与对象的变量的本质。这里,population属于Person类,因此是一个类的变量。name变量属于对象(它使用self赋值)因此是对象的变量。

    观察可以发现__init__方法用一个名字来初始化Person实例。在这个方法中,我们让population增加1,这是因为我们增加了一个人。同样可以发现,self.name的值根据每个对象指定,这表明了它作为对象的变量的本质。

    记住,你只能使用self变量来参考同一个对象的变量和方法。这被称为 属性参考 。

    在这个程序中,我们还看到docstring对于类和方法同样有用。我们可以在运行时使用Person.__doc__和Person.sayHi.__doc__来分别访问类与方法的文档字符串。

    就如同__init__方法一样,还有一个特殊的方法__del__,它在对象消逝的时候被调用。对象消逝即对象不再被使用,它所占用的内存将返回给系统作它用。在这个方法里面,我们只是简单地把Person.population减1。

    当对象不再被使用时,__del__方法运行,但是很难保证这个方法究竟在什么时候运行。如果你想要指明它的运行,你就得使用del语句,就如同我们在以前的例子中使用的那样。

    给C++/Java/C#程序员的注释
    Python中所有的类成员(包括数据成员)都是公共的 ,所有的方法都是有效的。
    只有一个例外:如果你使用的数据成员名称以双下划线前缀 比如__privatevar,Python的名称管理体系会有效地把它作为私有变量。
    这样就有一个惯例,如果某个变量只想在类或对象中使用,就应该以单下划线前缀。而其他的名称都将作为公共的,可以被其他类/对象使用。记住这只是一个惯例,并不是Python所要求的(与双下划线前缀不同)。
    同样,注意__del__方法与destructor的概念类似。

    继承

    面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

    假设你想要写一个程序来记录学校之中的教师和学生情况。他们有一些共同属性,比如姓名、年龄和地址。他们也有专有的属性,比如教师的薪水、课程和假期,学生的成绩和学费。

    你可以为教师和学生建立两个独立的类来处理它们,但是这样做的话,如果要增加一个新的共有属性,就意味着要在这两个独立的类中都增加这个属性。这很快就会显得不实用。

    一个比较好的方法是创建一个共同的类称为SchoolMember然后让教师和学生的类继承 这个共同的类。即它们都是这个类型(类)的子类型,然后我们再为这些子类型添加专有的属性。

    使 用这种方法有很多优点。如果我们增加/改变了SchoolMember中的任何功能,它会自动地反映到子类型之中。例如,你要为教师和学生都增加一个新的 身份证域,那么你只需简单地把它加到SchoolMember类中。然而,在一个子类型之中做的改动不会影响到别的子类型。另外一个优点是你可以把教师和 学生对象都作为SchoolMember对象来使用,这在某些场合特别有用,比如统计学校成员的人数。一个子类型在任何需要父类型的场合可以被替换成父类 型,即对象可以被视作是父类的实例,这种现象被称为多态现象。

    另外,我们会发现在重用父类的代码的时候,我们无需在不同的类中重复它。而如果我们使用独立的类的话,我们就不得不这么做了。

    在上述的场合中,SchoolMember类被称为基本类或超类 。而Teacher和Student类被称为导出类或子类 。

    现在,我们将学习一个例子程序。
    使用继承

    #!/usr/bin/python
    #
     Filename: inherit.py

    class SchoolMember:
        
    '''Represents any school member.'''
        
    def __init__(self, name, age):
            self.name 
    = name
            self.age 
    = age
            
    print '(Initialized SchoolMember: %s)' % self.name

        
    def tell(self):
            
    '''Tell my details.'''
            
    print 'Name:"%s" Age:"%s"' % (self.name, self.age),

    class Teacher(SchoolMember):
        
    '''Represents a teacher.'''
        
    def __init__(self, name, age, salary):
            SchoolMember.
    __init__(self, name, age)
            self.salary 
    = salary
            
    print '(Initialized Teacher: %s)' % self.name

        
    def tell(self):
            SchoolMember.tell(self)
            
    print 'Salary: "%d"' % self.salary

    class Student(SchoolMember):
        
    '''Represents a student.'''
        
    def __init__(self, name, age, marks):
            SchoolMember.
    __init__(self, name, age)
            self.marks 
    = marks
            
    print '(Initialized Student: %s)' % self.name

        
    def tell(self):
            SchoolMember.tell(self)
            
    print 'Marks: "%d"' % self.marks

    = Teacher('Mrs. Shrividya'4030000)
    = Student('Swaroop'2275)

    print # prints a blank line

    members 
    = [t, s]
    for member in members:
        member.tell() 
    # works for both Teachers and Students输出

    $ python inherit.py
    (Initialized SchoolMember: Mrs. Shrividya)
    (Initialized Teacher: Mrs. Shrividya)
    (Initialized SchoolMember: Swaroop)
    (Initialized Student: Swaroop)

    Name:
    "Mrs. Shrividya" Age:"40" Salary: "30000"
    Name:
    "Swaroop" Age:"22" Marks: "75"

    为 了使用继承,我们把基本类的名称作为一个元组跟在定义类时的类名称之后。然后,我们注意到基本类的__init__方法专门使用self变量调用,这样我 们就可以初始化对象的基本类部分。这一点十分重要——Python不会自动调用基本类的constructor,你得亲自专门调用它。

    我们还观察到我们在方法调用之前加上类名称前缀,然后把self变量及其他参数传递给它。

    注意,在我们使用SchoolMember类的tell方法的时候,我们把Teacher和Student的实例仅仅作为SchoolMember的实例。

    另 外,在这个例子中,我们调用了子类型的tell方法,而不是SchoolMember类的tell方法。可以这样来理解,Python总是首先查找对应类 型的方法,在这个例子中就是如此。如果它不能在导出类中找到对应的方法,它才开始到基本类中逐个查找。基本类是在类定义的时候,在元组之中指明的。

    一个术语的注释——如果在继承元组中列了一个以上的类,那么它就被称作多重继承 。

    字符串

    字符串是字符的序列。字符串基本上就是一组单词。

    我几乎可以保证你在每个Python程序中都要用到字符串,所以请特别留心下面这部分的内容。下面告诉你如何在Python中使用字符串。

    * 使用单引号(')

    你可以用单引号指示字符串,就如同'Quote me on this'这样。所有的空白,即空格和制表符都照原样保留。
    * 使用双引号(")

    在双引号中的字符串与单引号中的字符串的使用完全相同,例如"What's your name?"。
    * 使用三引号('''或""")

    利用三引号,你可以指示一个多行的字符串。你可以在三引号中自由的使用单引号和双引号。例如:

    '''This is a multi-line string. This is the first line.
    This is the second line.
    "What's your name?," I asked.
    He said "Bond, James Bond."
    '''

    * 转义符

    假 设你想要在一个字符串中包含一个单引号('),那么你该怎么指示这个字符串?例如,这个字符串是What's your name?。你肯定不会用'What's your name?'来指示它,因为Python会弄不明白这个字符串从何处开始,何处结束。所以,你需要指明单引号而不是字符串的结尾。可以通过 转义符 来完成这个任务。你用/'来指示单引号——注意这个反斜杠。现在你可以把字符串表示为'What/'s your name?'。

    另一个表示这个特别的字符串的方法是"What's your name?",即用双引号。类似地,要在双引号字符串中使用双引号本身的时候,也可以借助于转义符。另外,你可以用转义符//来指示反斜杠本身。

    值得注意的一件事是,在一个字符串中,行末的单独一个反斜杠表示字符串在下一行继续,而不是开始一个新的行。例如:

    "This is the first sentence./
    This is the second sentence."

    等价于"This is the first sentence. This is the second sentence."
    * 自然字符串

    如果你想要指示某些不需要如转义符那样的特别处理的字符串,那么你需要指定一个自然字符串。自然字符串通过给字符串加上前缀r或R来指定。例如r"Newlines are indicated by /n"。
    * Unicode字符串

    Unicode 是书写国际文本的标准方法。如果你想要用你的母语如北印度语或阿拉伯语写文本,那么你需要有一个支持Unicode的编辑器。类似地,Python允许你 处理Unicode文本——你只需要在字符串前加上前缀u或U。例如,u"This is a Unicode string."。

    记住,在你处理文本文件的时候使用Unicode字符串,特别是当你知道这个文件含有用非英语的语言写的文本。
    * 字符串是不可变的

    这意味着一旦你创造了一个字符串,你就不能再改变它了。虽然这看起来像是一件坏事,但实际上它不是。我们将会在后面的程序中看到为什么我们说它不是一个缺点。
    * 按字面意义级连字符串

    如果你把两个字符串按字面意义相邻放着,他们会被Python自动级连。例如,'What/'s' 'your name?'会被自动转为"What's your name?"。

    给C/C++程序员的注释
    在Python中没有专门的char数据类型。确实没有需要有这个类型,我相信你不会为此而烦恼。

    给Perl/PHP程序员的注释
    记住,单引号和双引号字符串是完全相同的——它们没有在任何方面有不同。

    给正则表达式用户的注释
    一定要用自然字符串处理正则表达式。否则会需要使用很多的反斜杠。例如,后向引用符可以写成'//1'或r'/1'。

    私有变量

    Python 对私有类成员有部分支持。任何象__spam这样形式的标识符(至少有两个前导下划线,至多有一个结尾下划线)目前被替换成 _classname__spam,其中classname是所属类名去掉前导下划线的结果。这种搅乱不管标识符的语法位置,所以可以用来定义类私有的实 例、变量、方法,以及全局变量,甚至于保存对于此类是私有的其它类的实例。如果搅乱的名字超过255个字符可能会发生截断。在类外面或类名只有下划线时不 进行搅乱。

    名字搅乱的目的是给类一种定义“私有”实例变量和方法的简单方法,不需担心它的其它类会定义同名变量,也不怕类外的代码弄乱实例 的变量。注意搅乱规则主要是为了避免偶然的错误,如果你一定想做的话仍然可以访问或修改私有变量。这甚至是有用的,比如调试程序要用到私有变量,这也是为 什么这个漏洞没有堵上的一个原因。(小错误:导出类和基类取相同的名字就可以使用基类的私有变量)。

    注意传递给exec,eval()或 evalfile()的代码不会认为调用它们的类的类名是当前类,这与global语句的情况类似,global的作用局限于一起字节编译的代码。同样的 限制也适用于getattr() ,setattr()和delattr(),以及直接访问__dict__的时候。

    下面例子中的类实现了自己的__getattr__和__setattr__方法,把所有属性保存在一个私有变量中,这在Python的新旧版本中都是可行的:

    class VirtualAttributes:
        
    __vdict = None
        
    __vdict_name = locals().keys()[0]
         
        
    def __init__(self):
            self.
    __dict__[self.__vdict_name= {}
        
        
    def __getattr__(self, name):
            
    return self.__vdict[name]
        
        
    def __setattr__(self, name, value):
            self.
    __vdict[name] = value
  • 相关阅读:
    程序集、属性和元数据
    wget 使用探索
    为wget使用代理
    创建项目 (Visual C#)
    wget 下载整个网站,或者特定目录
    演练:使用 Visual C# 创作用户控件
    NavigateUrl属性绑定 天高地厚
    【转载】Repeater控件里的大智慧 天高地厚
    【转载】ASP.NET Page 那点事 天高地厚
    意向锁如何提高性能 天高地厚
  • 原文地址:https://www.cnblogs.com/richard403/p/2793035.html
Copyright © 2020-2023  润新知