1. 关于 self

    和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

    经常把 java, python, shell 的语法弄混,我知道还是写的少。

    class Stu(object):
       def __init__(self, name, score):
          self.name = name
          self.score = score
       def scoreGrade(self, score): # 一开始,用 Java 的思维写成了 `def scoreGrade(score)` 因为这里的 score 只是形参罢了。但是 Python 规定类中的函数必须以**实例变量** `self` 作为函数的第一个参数。
          if score >= 90:
             return 'A'
          elif score >= 80:
             return 'B'
          else:
             return 'C'
       def __printInfo__(self):
          print '%s got %s' % (self.name, self.scoreGrade(self.score))
    
    stu1 = Stu('helen', 100)
    stu2 = Stu('ply', 89)
    stu1.__printInfo__()
    stu2.__printInfo__()
    

    # 方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据。所以,上面的代码还可以写成这样:
    class Stu(object):
       def __init__(self, name, score):
          self.name = name
          self.score = score
       def scoreGrade(self): # 既然可以访问,就不传参数了!
          if self.score >= 90:
             return 'A'
          elif self.score >= 80:
             return 'B'
          else:
             return 'C'
       def __printInfo__(self):
          print '%s got %s' % (self.name, self.scoreGrade())
    
    stu1 = Stu('helen', 100)
    stu2 = Stu('ply', 89)
    stu1.__printInfo__()
    stu2.__printInfo__()
    
    # 运行结果都是:
    helen got A
    ply got B 
    
  2. 实例变量(无中生有的实例变量!)

    和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

    >>> class Stu(object):
    ...    def __init__(self, name, score):
    ...       self.name = name
    ...       self.score = score
    ...    def scoreGrade(self):
    ...       if self.score >= 90:
    ...          return 'A'
    ...       elif self.score >= 80:
    ...          return 'B'
    ...       else:
    ...          return 'C'
    ...    def __printInfo__(self):
    ...       print '%s got %s' % (self.name, self.scoreGrade())
    ... 
    >>> stu1 = Stu('helen', 100)
    >>> stu2 = Stu('ply', 89)
    >>> stu1.age = 24 
    >>> stu1.age
    24
    >>> stu2.age # stu2 只有 name 和 score 两个变量
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Stu' object has no attribute 'age'
    
  3. 私有变量

    class Stu(object):
       def __init__(self, name, score):
          self.__name = name
          self.score = score
       def scoreGrade(self):
          if self.score >= 90:
             return 'A'
          elif self.score >= 80:
             return 'B'
          else:
             return 'C'
       def __printInfo__(self):
          print '%s got %s' % (self.name, self.scoreGrade())
    
    stu1 = Stu('helen', 100)
    stu2 = Stu('ply', 89)
    stu1.score = 99
    print stu1.score
    print stu2.__name
    

    # 运行结果
    99
    Traceback (most recent call last):
      File "sixth.py", line 19, in <module>
        print stu2.__name
    AttributeError: 'Stu' object has no attribute '__name'
    

  • 这里需要注意:

    class Stu(object):
       def __init__(self, name, score):
          self.__name = name
          self.score = score
       def scoreGrade(self):
          if self.score >= 90:
             return 'A'
          elif self.score >= 80:
             return 'B'
          else:
             return 'C'
       def __printInfo__(self):
          print '%s got %s' % (self.__name, self.scoreGrade())
    
    stu1 = Stu('helen', 100)
    stu2 = Stu('ply', 89)
    stu1.score = 99
    print stu1.score
    stu2.__name = 'Ply'
    stu2.__printInfo__() # 返回 ply got B。由此可知,上面的 stu2.__name = 'Ply' 一定是将 __name 作为另一个 public 变量来用了,不然覆值的时候就应该报错了!private 变量既不能查看更加不能修改。
    
    99
    ply got B
    
  1. 动态语言(Python) —— 不严谨的多态

    多态,顾名思义就是多个状态的概念。例如说 Stu 是一个类(模板),[Boy, Girl](我这里不考虑第三种人)。

    class Stu(object):
       def play(self): # 不要忘记 self
          print 'student is playing.'
       
    class Boy(Stu): # Boy 是 Stu 的子类
       def play(self):
          print 'Boys are playing basketball.'
       
    class Girl(Stu): # Girl 是 Stu 的子类
       def play(self):
          print 'Girls are playing piano.'
       
    class Baby(object):
       def play(self):
          print 'Baby is playing food.'
    
    def playSth(Stu):
       Stu.play()
    
    Bob = Boy()
    playSth(Bob)
    Lisa = Girl()
    playSth(Lisa)
    Lily = Baby()
    playSth(Lily)
    
    # 运行结果
    Bys are playing basketball.
    Girls are playing piano.
    Baby is playing food.
    

    可以看出即便 Baby 这个类有没有继承 Stu 这个类一点都不会影响到多态的运行结果。只要某个类中有 play(self) 这个方法就可以作为 playSth() 这个函数的参数。JAVA 作为静态语言就不是这样的。所以我觉得 JAVA 的多态更加严谨,但是 Python 的确实更加方便。

    如果想要实现像 JAVA 那样的多态效果该怎样做呢?那么体现在上面的例子中就是不想让 Baby 这个实例作为 playSth() 这个函数的参数呗。我们可以加一个判断语句:

    def playSth(stu):
    if isinstance(stu, Stu): # 判断参数 stu 是否是 Stu 类型的。
       stu.play()
    
  2. 关于 type() & isinstance() 的用法

    • 获取对象类型
      • 基本类型

        >>> type(1)
        <type 'int'>
        >>> type('a')
        <type 'str'>
        >>> type(True)
        <type 'bool'>
        >>> type((1,2,3))
        <type 'tuple'>
        >>> type([1,2,3])
        <type 'list'>
        >>> type({'a':1, 'b':2})
        <type 'dict'>
        >>> type(None)
        <type 'NoneType'>
        

        >>> isinstance(123, int)  
        True
        
      • 类的实例 —— 对象

        >>> class A(object):
        ...    pass
        >>> a = A()
        >>> type(a)
        <class '__main__.A'>
        
      • 判断一个对象是否是函数

        >>> import types
        >>> def fn():
        ...    pass
        ... 
        >>> type(fn) == types.FunctionType
        True
        >>> type(abs) == types.BuiltinFunctionType
        True
        >>> type(lambda x: x) == types.LambdaType
        True
        >>> type(x for x in range(6)) == types.GeneratorType
        

        >>> isinstance(abs, types.FunctionType)
        False
        
      • 判断某个对象和某个类之间是否继承关系

        >>> class Animal(object):
        ...    pass
        ... 
        >>> class Dog(Animal):
        ...    pass
        ... 
        >>> class Husky(Dog):
        ...    pass
        ...
        >>> A = Animal()
        >>> D = Dog()
        >>> H = Husky()
        >>> isinstance(H, Husky)
        True
        >>> isinstance(H, Dog)
        True
        >>> isinstance(H, Animal)
        True
        >>> isinstance(D, Dog) and isinstance(D, Animal)
        True
        
      • 判断一个变量是否是一些类型中的一个

        >>> isinstance(D, (Husky, Dog, Animal))
        True
        

        很显然,每次判断后的结果都是以 or 连接的。

  3. 外部获取私有属性(数据,方法)的办法

    >>> class Stu(object):
    ...    def __init__(self, name, age):
    ...       self.name = name
    ...       self.__age = age
    ... 
    >>> s1 = Stu('a', 1)
    >>> s1.age
    >>> dir(s1)
    ['_Stu__age', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
    >>> getattr(s1, '_Stu__age')
    1
    >>> setattr(s1, '_Stu__age', 18)
    >>> getattr(s1, '_Stu__age')
    18
    
  4. 类属性

    接着上面的示例继续说,

    >>> setattr(s1, 'gender', 'female')
    >>> s2 = Stu('Bob', 25)
    >>> getattr(s1, 'sex')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Stu' object has no attribute 'sex'
    >>> getattr(s1, 'gender')
    'female'
    >>> getattr(s2, 'gender')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Stu' object has no attribute 'gender'
    

    给 s1 额外添加的属性,其它的实例是没有的,如果想给某个类的实例都添加这个属性的话用这个方法就太 stupid 了。聪明的你,可以使用类属性。

    >>> class Stu(object):
    ...    gender = 'male'
    ...    def __init__(self, name, score):
    ...       self.Name = name
    ...       self.Score = score
    ... 
    >>> s1 = Stu('Lisa', 99)
    >>> s1.gender
    'male'
    >>> Stu.gender
    'male'
    >>> s1.gender = 'female'
    >>> getattr(s1, gender)  # 忘记加引号的结果!O.O
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'gender' is not defined
    >>> dir(s1)
    ['Name', 'Score', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender']
    >>> getattr(s1, 'gender')
    'female'
    >>> del s1.gender  # 删除实例 s1 的 gender 属性(实例属性)。
    >>> getattr(s1, 'gender') # 获取到的就是作为类属性的值了。
    'male'
    >>> Stu.gender = 'girl'
    >>> s1.gender
    'girl'
    >>> del Stu.gender # 删除类 Stu 的 gender 属性(类属性)。
    >>> s1.gender
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Stu' object has no attribute 'gender'
    >>> setattr(Stu, 'gender', 'boy') # 在外部设置类属性
    >>> s1.gender
    'boy'
    
  5. slots:用 slots 限制实例,并不能继承到子类当中。

    关于 __slots__ 需要注意的事情: * 如果 init 中初始化的属性没有出现在 slots 邦定的属性中,那么就无法创建对象了。

    ~~~ python  »> class Stu(object): … def init(self, name, age): … self.name = name … self.age = age … slots = (‘age’, ‘score’) …  »> s = Stu(‘Lisa’, 21) Traceback (most recent call last): File “", line 1, in File "", line 3, in __init__ AttributeError: 'Stu' object has no attribute 'name' ~~~

    • slots 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

      >>> class Girls(Stu):
      ...    pass
      ... 
      >>> g = Girls('Lisa', 22)
      >>> g.gender = 'female'
      >>> g.gender
      'female'
      
    • 如果在子类中也定义了 slots,那么 slots(子类) = slots(父类) + slots(子类)

      >>> class Boys(Stu):
      ...    __slots__ = ('name', 'gender')
      ... 
      >>> b = Boys('Bob', 12)
      >>> b.age
      12
      >>> b.name
      'Bob'
      >>> b.gender
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: gender
      >>> b.gender = 'male'
      >>> b.grade = 6
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'Boys' object has no attribute 'grade'