Python3学习笔记之面向对象编程

面向对象编程


参考链接:廖雪峰的官方网站


  • 面向对象编程
    • 类和实例
    • 访问限制
    • 继承和多态
    • 获取对象信息
      • 使用type()来判断对象类型,它返回对应的Class类型。
      • 使用isinstance()来判断对象是否是某种类型
      • 使用dir()来获得一个对象的所有属性和方法
    • 实例属性和类属性


类和实例

代码示例:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))
  • 通过class关键字来定义类,类名通常要大写,后面的括号里面表示该类继承于哪个类,所有类最终继承于object类,这类似于Java
  • python允许对实例变量绑定任何数据,因此同一个类的不同实例拥有的变量名称可能会不同
  • 特殊的__init__方法可以把必须绑定给类实例的属性填写进去,它的第一个参数永远是self,表示创建的实例本身,在创建实例的时候,必须传入与__init__方法匹配的参数,self除外
  • 实际上,Python中类中定义的函数的第一个参数都必须是self,调用时不用传递该参数
    对Student类使用实例代码:
chen = Student('chen', 13)
bart = Student('bart', 81)
chen.print_score()
bart.print_score()
chen.score = 100
bart.name = 'kobe'
chen.print_score()
bart.print_score()

输出结果:

chen: 13
bart: 81
chen: 100
kobe: 81

访问限制

  • 在内部属性的前面加上两个下划线__把它变成private,外部无法直接访问这个属性:
class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'Student' object has no attribute '__name'
  • 以一个下划线开头的变量_name外部可以访问,但是最好把它视为私有变量,不要随便访问
  • python解释器对外把__name改成了_Student__name,因此可以通过_Student__name来访问私有的内部属性(不建议采用这种方式):
>>> bart._Student__name
'Bart Simpson'
  • 我们在前面发现无法输出bart.__name,会报错Student类不含有此成员,但是bart.__name = 'New Name'语句是不会报错的,随后再输出bart.__name也不会再报错了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'

这是一种错误的写法,这样设置的__name变量并不是类内部的__name变量,相当于给bart新增了一个变量

  • 以双下划线开头,并以双下划线结尾的变量__xxx__,是特殊变量而不是私有变量,可以直接访问

继承和多态

python中继承和多态的概念与C++中类似,但python作为动态语言,有一种新的用法:
我们常利用多态来实现更方便的函数调用,如:

def run_twice(animal):
    animal.run()
    animal.run()

在C++或者Java中,我们如果传递的是Animal类的子类,调用的run()方法就会是子类的run()方法,这样我们在新增了Animal类的一个子类时无需修改依赖Animal类的run_twice函数。
在静态语言中这种用法要求我们传递的必须是Animal类或它的子类,但在python中没有这种限制,只要传递的类中有run()函数即可,称之为“鸭子类型

获取对象信息

使用type()来判断对象类型,它返回对应的Class类型。

  • 基本类型可以使用type()来判断:
>>> type(123)
<class 'int'>
>>> type(2.0)
<class 'float'>
>>> type(["sd",'s',12])
<class 'list'>
>>> type(None)
<class 'NoneType'>
  • 函数和类也可以使用type()来判断:
>>> class Person:
...     pass
... 
>>> def test():
...     pass
... 
>>> p = Person()
>>> type(p)
<class '__main__.Person'>
>>> type(test)
<class 'function'>
  • 利用type来作为if的判断条件,当类型不是int,str等基本类型时,需要使用types模块中定义的常量:
>>> 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(10)))==types.GeneratorType
True

使用isinstance()来判断对象是否是某种类型

  • isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上
  • 能用type()判断的基本类型也可以用isinstance()判断:
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
  • isinstance不仅可以判断是否是某种类型,还可以判断是否是某些类型的一种
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

使用dir()来获得一个对象的所有属性和方法

  • 获取str的所有属性和方法:
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
  • 利用getattr()setattr()以及hasattr(),我们可以直接操作一个对象,不过这几个函数是在不知道对象信息的时候使用的,在了解对象信息时没有必要使用:
>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()

>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
  • 对于getattr()函数,可以在传入的属性参数后面加一个默认的参数,这样在对象不含有这个属性时就会返回默认参数,而不是抛出异常了:
# 获取属性'z',如果不存在,返回默认值404
>>> getattr(obj, 'z', 404) 
404
  • getattr()函数还可以用于获取方法,把获取的方法赋值给一个变量,那个变量就指向这个方法,调用那个变量就相当于调用了这个方法:
>>> getattr(obj, 'power') # 获取属性'power'
0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

实例属性和类属性

实例属性我们在之前就已经使用过了,我们也了解到在python中可以给一个类实例绑定任何属性,方法是在类方法中利用self或者直接通过实例来绑定。

要给类绑定一个属性的话,可以在类中直接定义它:

class Student(object):
    name = 'Student'

也可以把类属性成为类的静态成员变量,这个属性是归类所有的,所有实例可以共享它。
需要注意的是,虽然name属性归类Student所有,但是类的所有实例都可以访问到,并且实例属性的优先级比类属性高,所以如果实例绑定了一个与类属性同名的实例属性时,优先调用的是实例属性:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

你可能感兴趣的:(Python3学习笔记)