python面向对象语法

面向对象编程

  • 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

  • 在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念

  • 例如:

    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)
    lisa = Student('Lisa Simpson', 87)
    bart.print_score()
    lisa.print_score()        
    
  • 面向对象的三大特征:封装,继承,多态

1.1类和实例

  • class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
class Student(object):
    pass
  • 创建实例是通过类名+()实现的:

    >>> bart = Student()
    >>> bart
    <__main__.Student object at 0x10a67a590>
    >>> Student
    <class '__main__.Student'>
    

    可以看到,变量bart指向的就是一个Student的实例,后面的0x10a67a590是内存地址,每个object的地址都不一样,而Student本身则是一个类。

  • 把一些我们认为必须绑定的属性强制填写进类中。通过定义一个特殊的__init__方法,在创建实例的时候,就等属性绑上去:

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

    注意到__init__方法的第一个参数永远是self,表示创建的实例本身

  • 有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去

1.2访问限制

  • 实例的变量名如果以__开头,就变成了一个私有变量(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 "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute '__name'       
    
  • 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:

    >>> bart._Student__name
    'Bart Simpson'
    
    但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
    
  • 总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。

  • 最后注意下面的这种错误写法

    >>> bart = Student('Bart Simpson', 59)
    >>> bart.get_name()
    'Bart Simpson'
    >>> bart.__name = 'New Name' # 设置__name变量!
    >>> bart.__name
    'New Name'
    

    表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量。不信试试:

    >>> bart.get_name() # get_name()内部返回self.__name
    'Bart Simpson'
    
  • 而是通过get。set方法

    def get_score(self):
            return self.__score
    def set_score(self, score):
            self.__score = score       
    

1.3继承和多态

  • 例如

    class Animal(object):
        def run(self):
            print('Animal is running...')
            
    class Dog(Animal): #括号内是继承的对象	
        pass
    
    class Cat(Animal):
        pass        
    

    对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。CatDog类似。

  • 继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,DogCat作为它的子类,什么事也没干,就自动拥有了run()方法:

  • 当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

静态语言和动态语言:

  • 静态语言:java就是静态语言,如果参数要传入animal类型,则传入的对象必须是animal或者它的子类对象
  • 动态语言:python,就不一定传入animal类型了,我们只需要传入与animal有相同方法的对象即可

1.4获取对象信息

当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?

判断对象类型 type()
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
  • 判断是否是基本数据类型,直接写int,str等

    >>> type(123)==type(456)
    True
    >>> type(123)==int
    True
    >>> type('abc')==type('123')
    True
    >>> type('abc')==str
    True
    >>> type('abc')==type(123)
    False
    
  • 判断一个对象是否为函数,可以用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(d, Dog) and isinstance(d, Animal)
True

并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”

使用dir()
  • 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list

    >>> dir('ABC')
    ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill','upper']
    
  • 此方法可以查看出类中包含的方法。例如在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

    >>> len('ABC')
    3
    >>> 'ABC'.__len__()
    3
    

    我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:

    >>> class MyDog(object):
    ...     def __len__(self):
    ...         return 100
    ...
    >>> dog = MyDog()
    >>> len(dog)
    100
    
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

也可以获得对象的方法:

>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

1.5实例属性和类属性

类属性可以理解为静态属性?

  • 创建实例属性:

  • 实例绑定属性的方法是通过实例变量,或者通过self变量

    class Student(object):
        def __init__(self, name):
            self.name = name
    
    s = Student('Bob')
    s.score = 90
    
  • 创建类属性:

    • class Student(object):
          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
      

例子:

为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count+=1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值