zoukankan      html  css  js  c++  java
  • Python对象

    理解几个概念:

    1. 类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称作类的实例。
    2. 实例:也称对象。通过类定义的初始化方法,赋予具体的值,成为一个”有血有肉的实体”。
    3. 实例化:创建类的实例的过程或操作。
    4. 实例变量:定义在实例中的变量,只作用于当前实例。
    5. 类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。
    6. 数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
    7. 方法:类中定义的函数。
    8. 静态方法:不需要实例化就可以由类执行的方法
    9. 类方法:类方法是将类本身作为对象进行操作的方法。
    10. 方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override。
    11. 封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
    12. 继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
    13. 多态:根据对象类型的不同以不同的方式进行处理。

    调用类的三种方法

    实例方法,静态方法,类方法

    class dd:
        def __init__(self,url):
            self.url=url
        def runx(self):
            print requests.get(self.url).status_code
    
    a = dd('http://www.langzi.fun')
    a.runx()
    # 这种调用方法就是实例方法
    

      

    静态方法

    静态方法由类调用,无默认参数。将实例方法参数中的self去掉,然后在方法定义上方加上@staticmethod,就成为静态方法。它属于类,和实例无关。建议只使用类名.静态方法的调用方式。(虽然也可以使用实例名.静态方法的方式调用)

    class ff:
        @staticmethod
        def runx():
            print requests.get('http://www.langzi.fun').status_code
    ff.runx()


    类方法

    类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数。执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名.类方法的调用方式。(虽然也可以使用实例名.类方法的方式调用)

    class gg:
        url = 0
        stat = 0
        # 因为使用classmethod后会传入新的变量,所以一开始是需要自己先定义类变量
        def __init__(self,url=0,stat=0):
        # 这里按照正常的定义构造函数
            self.url=url
            self.stat=stat
        @classmethod
        # 装饰器,立马执行下面的函数
        def split(cls,info):
            # 这个函数接受两个参数,默认的cls就是这个类的init函数,info就是外面传入进来的
            url,stat=map(str,info.split('-'))
            # 这里转换成了格式化的结构
            data = cls(url,stat)
            # 然后执行这个类第一个方法,这个类构造函数需要传入两个参数,于是就传入了两个参数
            return data
            # 这里就直接返回了函数结果
        def outer(self):
            print self.url
            print self.stat
    
    r = gg.split(('langzi-200'))
    r.outer()
    # 这里是调用类方法,与调用实例方法一样

    类的特性

    封装

    封装是指将数据与具体操作的实现代码放在某个对象内部,外部无法访问。必须要先调用类的方法才能启动。

    案例

    class cc:
        ccc = 'ccc'
        # cc就是类名 如果想要继承别的类 就class cc(threading) 意思就是从threading继承
        def __init__(self,a,b,c):
            self.a=a
            self.b=b
            self.c=c
    print e.ccc
    #类变量,在类里面找到定义的变量。
    print ccc
    # 这里会报错,这就是封装。类中的函数同理。
    

    继承

    当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
    比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:

    class Animal(object):
        def run(self):
            print 'Animal is running...'
    

    当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:

    class Dog(Animal):
        pass
    class Cat(Animal):
        pass
    

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

    dog = Dog()
    dog.run()
    cat = Cat()
    cat.run()
    

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

    多态

    要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个Animal类型的变量:

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

    当我们传入Animal的实例时,run_twice()就打印出:

    run_twice(Animal())
    运行结果:
    Animal is running...
    Animal is running...
    

    当我们传入Dog的实例时,run_twice()就打印出:

    run_twice(Dog())
    运行结果:
    Dog is running...
    Dog is running...
    

    当我们传入Cat的实例时,run_twice()就打印出:

    run_twice(Cat())
    运行结果:
    Cat is running...
    Cat is running...
    

    看上去没啥意思,但是仔细想想,现在,如果我们再定义一个Tortoise类型,也从Animal派生:

    class Tortoise(Animal):
        def run(self):
            print 'Tortoise is running slowly...'
    

    当我们调用run_twice()时,传入Tortoise的实例:

    run_twice(Tortoise())
    运行结果:
    Tortoise is running slowly...
    Tortoise is running slowly...
    

    你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

    多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

    对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

    对扩展开放:允许新增Animal子类;
    对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
    总结:继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写;
    有了继承,才能有多态。在调用类实例方法的时候,尽量把变量视作父类类型,这样,所有子类类型都可以正常被接收;
    旧的方式定义Python类允许不从object类继承,但这种编程方式已经严重不推荐使用。任何时候,如果没有合适的类可以继承,就继承自object类。

    魔法方法

    在上面有提到除了init之外还有iter,reverse的方法,这里就详细说下除了init初始化还有哪些别的方法。

    __init__ :      构造函数,在生成对象时调用
    __del__ :       析构函数,释放对象时使用
    __repr__ :      打印,转换
    __setitem__ :   按照索引赋值
    __getitem__:    按照索引获取值
    __len__:        获得长度
    __cmp__:        比较运算
    __call__:       调用
    __add__:        加运算
    __sub__:        减运算
    __mul__:        乘运算
    __div__:        除运算
    __mod__:        求余运算
    __pow__:        幂
    

    具体使用

    1. doc

    说明性文档和信息。Python自建,无需自定义。

    class Foo:
        """ 描述类信息,可被自动收集 """
        def func(self):
            pass
    # 打印类的说明文档 
    print(Foo.__doc__)
    

    2. init()

    实例化方法,通过类创建实例时,自动触发执行。

    class Foo:
        def __init__(self, name):
            self.name = name
            self.age = 18
    obj = Foo(jack') # 自动执行类中的 __init__ 方法
    

    3. module__ 和 __class

    module 表示当前操作的对象在属于哪个模块。
    class 表示当前操作的对象属于哪个类。
    这两者也是Python内建,无需自定义。

    class Foo:
        pass
    obj = Foo()
    print(obj.__module__)
    print(obj.__class__)
    运行结果:
    main
    

    4. del()

    析构方法,当对象在内存中被释放时,自动触发此方法。

    注:此方法一般无须自定义,因为Python自带内存分配和释放机制,除非你需要在释放的时候指定做一些动作。析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    class Foo:
        def __del__(self):
            print("我被回收了!")
    
    obj = Foo()
    del obj
    

    5. call()

    如果为一个类编写了该方法,那么在该类的实例后面加括号,可会调用这个方法。

    注:构造方法的执行是由类加括号执行的,即:对象 = 类名(),而对于call() 方法,是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
        def __init__(self):
            pass
        def __call__(self, *args, **kwargs):
            print('__call__')
    obj = Foo()     # 执行 __init__
    obj()       # 执行 __call__
    

    可以用Python内建的callable()函数进行测试,判断一个对象是否可以被执行。

    callable(Student())
    

    运行结果:

    True
    

    6. dict

    列出类或对象中的所有成员!非常重要和有用的一个属性,Python自建,无需用户自己定义。

    class Province:
        country = 'China'
        def __init__(self, name, count):
            self.name = name
            self.count = count
        def func(self, *args, **kwargs):
            print('func')
    # 获取类的成员
    print(Province.__dict__)
    # 获取 对象obj1 的成员 
    obj1 = Province('HeBei',10000)
    print(obj1.__dict__)
    # 获取 对象obj2 的成员 
    obj2 = Province('HeNan', 3888)
    print(obj2.__dict__)
    

    7. str()

    如果一个类中定义了str()方法,那么在打印对象时,默认输出该方法的返回值。这也是一个非常重要的方法,需要用户自己定义。 

    下面的类,没有定义str()方法,打印结果是:

    class Foo:
        pass
    obj = Foo()
    print(obj)
    定义了__str__()方法后,打印结果是:'jack'。
    class Foo:
        def __str__(self):
            return 'jack'
    obj = Foo()
    print(obj)
    

    8、getitem__()、_setitem_()、__delitem()

    取值、赋值、删除这“三剑客”的套路,在Python中,我们已经见过很多次了,比如前面的@property装饰器。

    Python中,标识符后面加圆括号,通常代表执行或调用方法的意思。而在标识符后面加中括号[],通常代表取值的意思。Python设计了getitem()、setitem()、delitem()这三个特殊成员,用于执行与中括号有关的动作。它们分别表示取值、赋值、删除数据。

    也就是如下的操作:

    a = 标识符[] :   执行__getitem__方法
    标识符[] = a  :   执行__setitem__方法
    del 标识符[] :   执行__delitem__方法
    

    如果有一个类同时定义了这三个魔法方法,那么这个类的实例的行为看起来就像一个字典一样,如下例所示:

    class Foo:
        def __getitem__(self, key):
            print('__getitem__',key)
        def __setitem__(self, key, value):
            print('__setitem__',key,value)
        def __delitem__(self, key):
            print('__delitem__',key)
    obj = Foo()
    result = obj['k1']      # 自动触发执行 __getitem__
    obj['k2'] = 'jack'      # 自动触发执行 __setitem__
    del obj['k1']             # 自动触发执行 __delitem__
    

    9. iter()

    这是迭代器方法!列表、字典、元组之所以可以进行for循环,是因为其内部定义了 iter()这个方法。如果用户想让自定义的类的对象可以被迭代,那么就需要在类中定义这个方法,并且让该方法的返回值是一个可迭代的对象。当在代码中利用for循环遍历对象时,就会调用类的这个iter()方法。

    普通的类:

    class Foo:
        pass
    obj = Foo()
    for i in obj:
        print(i)
    # 报错:TypeError: 'Foo' object is not iterable<br># 原因是Foo对象不可迭代
    添加一个__iter__(),但什么都不返回:
    class Foo:
        def __iter__(self):
            pass
    obj = Foo()
    for i in obj:
        print(i)
    # 报错:TypeError: iter() returned non-iterator of type 'NoneType'
    #原因是 __iter__方法没有返回一个可迭代的对象
    

    返回一个个迭代对象:

    class Foo:
        def __init__(self, sq):
            self.sq = sq
        def __iter__(self):
            return iter(self.sq)
    obj = Foo([11,22,33,44])
    for i in obj:
        print(i)
    

    最好的方法是使用生成器:

    class Foo:
        def __init__(self):
            pass
        def __iter__(self):
            yield 1
            yield 2
            yield 3
    obj = Foo()
    for i in obj:
        print(i)
    

    10、len()

    在Python中,如果你调用内置的len()函数试图获取一个对象的长度,在后台,其实是去调用该对象的len()方法,所以,下面的代码是等价的:

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

    Python的list、dict、str等内置数据类型都实现了该方法,但是你自定义的类要实现len方法需要好好设计。

    11. repr()

    这个方法的作用和str()很像,两者的区别是str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。通常两者代码一样。

    class Foo:
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return "this is %s" % self.name
        __repr__ = __str__
    

    12. add__: 加运算 _sub_: 减运算 _mul_: 乘运算 _div_: 除运算 _mod_: 求余运算 __pow: 幂运算

    这些都是算术运算方法,需要你自己为类设计具体运算代码。有些Python内置数据类型,比如int就带有这些方法。Python支持运算符的重载,也就是重写。

    class Vector:
       def __init__(self, a, b):
          self.a = a
          self.b = b
       def __str__(self):
          return 'Vector (%d, %d)' % (self.a, self.b)
       def __add__(self,other):
          return Vector(self.a + other.a, self.b + other.b)
    v1 = Vector(2,10)
    v2 = Vector(5,-2)
    print (v1 + v2)
    

    13. author作者信息

    __author__ = "Jack"
    def show():
        print(__author__)
    show()
    

    14. slots

    Python作为一种动态语言,可以在类定义完成和实例化后,给类或者对象继续添加随意个数或者任意类型的变量或方法,这是动态语言的特性。例如:

    def print_doc(self):
        print("haha")
    
    class Foo:
        pass
    
    obj1 = Foo()
    obj2 = Foo()
    # 动态添加实例变量
    obj1.name = "jack"
    obj2.age = 18
    # 动态的给类添加实例方法
    Foo.show = print_doc
    obj1.show()
    obj2.show()
    

    但是!如果我想限制实例可以添加的变量怎么办?可以使slots限制实例的变量,比如,只允许Foo的实例添加name和age属性。

    def print_doc(self):
        print("haha")
    class Foo:
        __slots__ = ("name", "age")
        pass
    obj1 = Foo()
    obj2 = Foo()
    # 动态添加实例变量
    obj1.name = "jack"
    obj2.age = 18
    obj1.sex = "male"       # 这一句会弹出错误
    # 但是无法限制给类添加方法
    Foo.show = print_doc
    obj1.show()
    obj2.show()
    由于'sex'不在__slots__的列表中,所以不能绑定sex属性,试图绑定sex将得到AttributeError的错误。
    Traceback (most recent call last):
      File "F:/Python/pycharm/201705/1.py", line 14, in <module>
        obj1.sex = "male"
    AttributeError: 'Foo' object has no attribute 'sex'
    

    需要提醒的是,slots定义的属性仅对当前类的实例起作用,对继承了它的子类是不起作用的。想想也是这个道理,如果你继承一个父类,却莫名其妙发现有些变量无法定义,那不是大问题么?如果非要子类也被限制,除非在子类中也定义slots,这样,子类实例允许定义的属性就是自身的slots加上父类的slots。

    成员保护与访问机制

    有些对象你不想外部访问,即使是通过调用类对象也无法访问,那就请认真学完本章节。

    私有成员

    class obj:
        def __init__(self,name):
            self.name=name
        def pri(self):
            print self.name
        __age = 18
        # 加上双下划线的就是私有变量,只能在类的内部访问,外部无法访问
    a = obj('zhao')
    a.pri()
    

    运行结果:

    zhao
    

    如果要在类中调用这个私有成员,可以这么用

    class obj:
        def __init__(self,name):
            self.name=name
        def prin(self):
            print self.name
        __age = 18
        # 加上双下划线的就是私有变量,只能在类的内部访问,外部无法访问
        @classmethod
        # 如果要在类中调用,首先调用类方法
        def pri(cls):
            print cls.__age
            # 然后在使用
    a = obj('zhao')
    a.prin()
    obj.pri()
    # 通过这样直接调用类中的私有变量
    

    运行结果:

    zhao
    18
    

    使用get-set-del方法操作私有成员

    class obj:
        def __init__(self,name):
            self.name=name
        def prin(self):
            print self.name
        __age = 18
        # 加上双下划线的就是私有变量,只能在类的内部访问,外部无法访问
        @classmethod
        # 如果要在类中调用,首先调用类方法
        def pri(cls):
            print cls.__age
            # 然后在使用
        @classmethod
        def set_age(cls,value):
            cls.__age = value
            return cls.__age
            # 这个用法就是改变__age的值
        @classmethod
        def get_age(cls):
            return cls.__age
            # 这个用法就是直接返回__age的值
        @classmethod
        def del_age(cls):
            del cls.__age
            # 这个用法就是直接删除__age的值
    
    print obj.get_age()
    # 这里是直接调用出__age的值  返回值18
    print obj.set_age(20)
    # 这里是直接改变__age的值  返回值20
    obj.del_age()
    # 这里是直接删除__age的值
    

    思考: 既然是私有变量,不让外部访问,为何有要在后面调用又改变呢?因为可以对私有变量进行额外的检测,处理,加工等等。比如判断value的值,使用isinstance然后做if-else判断。

    使用私有变量可以对内部变量进行保护,外部无法改变,但是可以对它进行检测处理。

    这里引申一下私有成员的保护机制,使用__age对私有变量其实就是—>obj._obj__age的样子进行保护,说白了你直接使用obj._obj__age就可以直接调用内部私有变量age了。

    Propety装饰器

    把类的方法伪装成属性调用的方式,就是把类里面的一个函数,变成一个属性一样的东西~
    一开始调用类的方法要使用圆括号,现在变成了属性进行读取设置存储。
    举个例子来说明:

    常用的调用方法

    class obj:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
            # 讲这些设置成私有变量
        def get_age(self):
            return self.__age
        def set_age(self,value):
            if isinstance(value,int):
                self.__age=value
            else:
                raise ValueError('非整数类型')
        def del_age(self):
            print 'delete over'
    a = obj('langzi',18)
    print a.get_age()
    a.set_age(20)
    print a.get_age()
    

    使用装饰器

    class obj:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
            # 把这些设置成私有变量
        @property
        def age(self):
            return self.__age
        @age.setter
        def age(self,value):
            if isinstance(value,int):
                self.__age=value
            else:
                raise ValueError('非整数类型')
        @age.deleter
        def age(self):
            print 'delete over'
    a = obj('langzi',18)
    # 使用这些装饰器,可以使用类与对象的方法直接调用
    print a.age
    # 这里就是直接调用返回age的值
    a.age=20
    # 这里就是直接使用setter把值转换
    print a.age
    del a.age
    # 删除age
    

    当然这种调用方法有些麻烦,每次都是一个一个去实例类与对象,有个更加简单直观的方法。

    更加减半的使用property()函数

    除了使用装饰器的方式将一个方法伪装成属性外,Python内置的builtins模块中的property()函数,为我们提供了第二种设置类属性的手段。

    class People:
    
        def __init__(self, name, age):
            self.__name = name
            self.__age = age
    
        def get_age(self):
            return self.__age
    
        def set_age(self, age):
            if isinstance(age, int):
                self.__age = age
            else:
                raise ValueError
    
        def del_age(self):
            print("删除年龄数据!")
    
        # 核心在这句
        age = property(get_age, set_age, del_age, "年龄")    
    
    
    obj = People("jack", 18)
    print(obj.age)
    obj.age = 19
    print("obj.age:  ", obj.age)
    del obj.ag
    

    通过语句age = property(get_age, set_age, del_age, “年龄”)将一个方法伪装成为属性。其效果和装饰器的方法是一样的。

    property()函数的参数:

    第一个参数是方法名,调用 实例.属性 时自动执行的方法
    第二个参数是方法名,调用 实例.属性 = XXX时自动执行的方法
    第三个参数是方法名,调用 del 实例.属性 时自动执行的方法
    第四个参数是字符串,调用 实例.属性.__doc__时的描述信息。

    参考链接:https://zhuanlan.zhihu.com/p/61042358

  • 相关阅读:
    PAT顶级 1024 Currency Exchange Centers (35分)(最小生成树)
    Codeforces 1282B2 K for the Price of One (Hard Version)
    1023 Have Fun with Numbers (20)
    1005 Spell It Right (20)
    1092 To Buy or Not to Buy (20)
    1118 Birds in Forest (25)
    1130 Infix Expression (25)
    1085 Perfect Sequence (25)
    1109 Group Photo (25)
    1073 Scientific Notation (20)
  • 原文地址:https://www.cnblogs.com/ydbk/p/15074275.html
Copyright © 2011-2022 走看看