zoukankan      html  css  js  c++  java
  • Python菜鸟之路:Python基础-类(2)——成员、成员修饰符、异常及其他

    三大成员

      在Python的面向对象中,主要包括三大成员:字段、方法、属性

    字段

      类成员的字段又可分为普通字段、静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,代码示例如下:

            class Foo:
                #字段:静态字段
                CC = 123
                def __init__(self, name):
                    # 字段:普通字段
                    self.name = name
    访问普通字段:
    obj = Foo('bob')
    print(obj.name)
    out: bob

    访问静态字段:
    print(Foo.CC)
    out: 123

    1.普通字段:self.xxx

      从上边的例子可以看出,普通字段存储在对象中,需要通过对象去访问。

    2.静态字段:

      从上边的例子可以看出,静态字段写在方法外边,只属于类,保存在类里边,不需要实例化也能访问,可以直接通过类去访问。可以起到节省内存的作用,静态字段代码加载的时候已经在内存创建。当然了,也可以通过对象去访问,但是不推荐

    小结:

      普通字段只能用对象访问。静态字段用类访问(不建议用对象访问).且静态字段未做修改之前,对象共享一块内存地址。当对静态字段进行修改后,对象拥有自己的静态字段,不再共享

    方法

      方法分为普通方法、静态方法和类方法。这三种方法在内存中都归属于类。代码如下:

    class Foo:
        # 字段:静态字段
        CC = 123
    
        def __init__(self, name):
            # 字段:普通字段
            self.name = name
    
        # 普通方法
        def show(self):
            print(self.name)
    
        # 静态方法
    @staticmethod def f1(arg): print("静态方法" + arg) # 类方法 @classmethod def f2(cls): print("类方法") print(cls)
    # 调用普通方法:
    obj = Foo('bob')
    obj.show()
    out: bob
    # 调用静态方法;
    Foo.f1('Bob')
    out: 静态方法bob
    # 调用类方法:
    Foo.f2()
    out: 类方法 <class '__main__.Foo'>

    1. 普通方法:由对象调用

      至少包含一个self参数,并且在执行普通方法时,自动将调用该方法的对象赋值给self

    2. 静态方法:由类调用

      由staticmethod装饰,且无默认参数self,可以根据对应方法,接收任意参数。当方法内部不需要对象中封装的值时,可以将方法写成静态方法。

    3. 类方法:由类调用

      类方法是静态方法的特殊形式,至少一个包含cls参数;该参数不需要传递,会自动进行传递,执行类方法时,自动将调用该方法的类名赋值给cls。

    小结:

      相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

          不同点:方法调用者不同、调用方法时自动传入的参数不同。

    属性

       属性其实就是类中普通方法的变种。属性具有方法的写作形式,具有字段的访问方式,并且提供了一种关联方式。定义方法如下

    定义方式1:对方法使用装饰器

    class Pager:
        CC = 123
        def __init__(self, all_count):
            self.all_count = all_count
    
        @property
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
        @all_pager.setter
        def all_pager(self, value):
            self.all_count = value
            print(self.all_count)
    
        @all_pager.deleter
        def all_pager(self):
            print('del ' , Pager.CC)
    
    # 自动执行@property修饰的all_pager方法 obj
    = Pager(101) print(obj.all_pager) out: 11
    # 自动执行@all_pager.setter修饰的all_pager方法 obj.all_pager
    = 99 out: 99 print(obj.all_pager) out: 10
    # 自动执行@all_pager.deleter修饰的all_pager方法 del obj.all_pager out: del 123

    定义方式2:创建property对象

    class Pager:
        def __init__(self, all_count):
            self.all_count = all_count
    
        def f1(self):
            return 123
    
        def f2(self, value):
            print('执行f2 ' + value)
    
        def f3(self):
            print("执行f3")
    
        foo = property(fget=f1,fset=f2,fdel=f3)
    
    p = Pager(101)
    
    # 执行f1方法
    result = p.foo
    print(result)  # out: 123
    
    # 执行f2方法
    p.foo = 'xxxxx'   # out:  执行f2 xxxxx
    
    # 执行f3方法
    del p.foo  # out: 执行f3

    类成员修饰符

      类成员修饰符,顾名思义,就是用来修饰字段、方法和属性的。对于每一个类的成员,都有两种形式:共有成员和私有成员。

    私有成员和公有成员的定义不同

      私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)

    私有成员和公有成员的访问限制不同

    静态字段

    公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
    私有静态字段:仅类内部可以访问;

    class C:
        name = "公有静态字段"
        def func(self):
            print(C.name)
    
    class D(C):
        def show(self):
            print(C.name)
    
    print(C.name)         # 类访问
    out: 公有静态字段
    obj = C()
    obj.func()     # 类内部可以访问
    out: 公有静态字段
    obj_son = D()
    obj_son.show() # 派生类中可以访问
    out: 公有静态字段
    公有静态字段
    class C:
        __name = "公有静态字段"
        def func(self):
            print(C.__name)
    
    class D(C):
        def show(self):
            print(C.__name)
    
    print(C.__name)       # 类访问            ==> 错误
    out: AttributeError: type object 'C' has no attribute '__name'
    # obj = C()
    obj.func()     # 类内部可以访问     ==> 正确
    # out: 公有静态字段
    obj_son = D()
    obj_son.show() # 派生类中可以访问   ==> 错误
    # out: AttributeError: type object 'C' has no attribute '_D__name'
    私有静态字段

    普通字段

    公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
    私有普通字段:仅类内部可以访问;代码同静态字段的测试方法是一样,这里不再距离。

    ps:非要访问私有属性的话,可以通过 "对象._类名__属性名"的方式来访问。代码如下:

    class C:
        __name = "公有静态字段"
        def func(self):
            print(C.__name)
    
    class D(C):
        def show(self):
            print(C.__name)
    
    obj = D()
    print(obj._C__name)
    out: 公有静态字段
    强制访问私有字段

    类特殊成员

      常用的类特殊成员如下

    1. __init__  构造方法,类在实例化的时候自动执行
    2. __doc__ 表示类的描述信息
    3. __module__ 表示当前操作的对象在哪个模块
    4. __class__    表示当前操作的对象的类是什么
    5. __call__ 对象后面加括号,触发执行__call__方法。比如A=Foo(), A()会自动出发__call__方法。
    6. __str__ 该方法return什么,print(obj)的时候自动调用该方法,否则返回对象地址。或者执行str() ,自动调用该方法
    7. __del__ 析构方法,一般不需要自己实现。在类在被垃圾回收机制清理之前, 会自动执行该方法。
    8. __add__ 一个对象加另外一个对象,就自动执行 ret = obj1 +obj2. 知道就行,很少用到
    9. __dict__ 不用写,默认已经有一个dict。 obj.__dict__ . 获取对象中封装的数据

    10. __setitem__, __getitem__, __delitem__

    方式一:输入类型为str

    class Foo(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __getitem__(self, key):
            print('__getitem__',key)
    
        def __setitem__(self, key, value):
            print('__setitem__',key,value)
            # print(value[key])
    
        def __delitem__(self, key):
            print('__delitem__',key)
    
    obj = Foo('Boss', 18)
    # 执行obj['...']的时候执行__getitem__
    obj['Baby']
    out: __getitem__ Baby
    
    # 执行obj['...'] = ...的时候执行__setitem__
    obj['Baby'] = 1123
    out: __setitem__ Baby 1123
    
    # 执行del obj['...']的时候执行__delitem__
    del obj['XXOO']
    out: __delitem__ XXOO
    str:__getitem__、__setitem__、__delitem__

    方式二:输入类型为slice,此时key具有三个字段为[start:stop:step],测试代码如下

    class Foo(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __getitem__(self, key):
            print(type(key))  # 类型有两种: str或者 slice
            # 如果是输入类型是slice
            # print(key.start)
            # print(key.stop)
            # print(key.step)
            # key.step
            print('__getitem__',key)
    
        def __setitem__(self, key, value):
            print('__setitem__',key,value)
            print(value[key])
    
        def __delitem__(self, key):
            print(type(key), key)
            # print(key.start)
            # print(key.stop)
            # print(key.step)
            print('__delitem__',key)
    
    # obj[key] 自动调用__getitem__
    obj = Foo('alex', 18)
    obj[1:4:2]
    out: 
    <class 'slice'>
    __getitem__ slice(1, 4, 2)
    
    # obj[key] = ... 自动调用__setitem__
    obj[1:4] = [1,2,3,4,5,6,7]
    out:
    __setitem__ slice(1, 4, None) [1, 2, 3, 4, 5, 6, 7]
    [2, 3, 4]
    
    # del obj[key] 自动调用__delitem__
    del obj[1:4:2]
    out:
    <class 'slice'> slice(1, 4, 2)
    __delitem__ slice(1, 4, 2)
    slice:__getitem__、__setitem__、__delitem__

    11.__iter__  对象通常不能被迭代,因此此方法可以使对象可迭代,该方法返回可迭代对象

    class Foo(object):
        def __iter__(self):
            # return iter([11,22,33,44,55])
            yield 1
            yield 2
    
    obj = Foo()
    for item in obj:
        print(item)
    
    out:
    1
    2
    __iter__

    面向对象的其他知识点

    1. isinstance(a, b) 查看a是不是b的实例,或者是b的父类的实例。

    2. issubclass(a, b) 查看a是否b的子类

    3. 应用:利用类的继承关系,实现有序字典的类

    class MyDict(dict):
    
        def __init__(self):
            # 执行父类的init方法
            super(MyDict, self).__init__()
            self.li = []
    
        def __setitem__(self, key, value):
            self.li.append(key)
            super(MyDict, self).__setitem__(key, value)
    
        def __getitem__(self, item):
            pass
    
        def __str__(self):
            temp_list = []
            for key in self.li:
                value = self.get(key)
                temp_list.append("'%s':%s" % (key, value))
            return '{' + ",".join(temp_list) + '}'
    
    
    obj = MyDict()
    obj['k1'] = 123
    obj['k2'] = 456
    print(obj)
    out: {'k1':123,'k2':456}
    自定义实现简单有序dict

    4. 如何执行父类的方法

    class C1:
        def f1(self):
            print('C1.f1')
    
    class C2(C1):
        def f1(self):
            # 主动去执行父类的F1方法
            super(C2, self).f1()
            print('C2.f1')
    
    obj = C2()
    obj.f1()
    out:
    C1.f1
    C2.f1
    执行父类:super的用法

    5. python2中的继承(待补)

    其他

    设计模式之单例模式

      单例模式可以用类方法或者静态方法来实现

    案例:假设有个连接池,用户如果首次去连接,则创建一个池对象,返回给用户或者线程。如果在用户或线程连接的时候,该池对象已经存在,那么则直接返回。当然,也可以定义一个计数器,计算当前已分配地址池和总地址池的关系。代码如下:

    class Foo:
        instance = None
    
        def __init__(self,name):
            self.name = name
    
        @classmethod
        def get_instance(cls, name):
            if cls.instance:
                return cls.instance
            else:
                obj = cls(name)
                cls.instance = obj
                return obj
    
    obj1 = Foo.get_instance('alex')
    print(obj1)
    obj2 = Foo.get_instance('bbb')
    print(obj2)
    out:
    <__main__.Foo object at 0x0000000000B246A0>
    <__main__.Foo object at 0x0000000000B246A0>
    # 返回相同的地址,说明他们共用一个池对象
    单例模式

    异常处理

      所以异常处理,就是在可预知或者不可预知的情况下,提前捕捉异常,甚至针对异常进行的一种或一类操作。这里就用到了try语句。try语句的完整语法如下:

    try:
        # 捕捉这段代码的异常情况
        ...
    except:
        # 检测到异常的处理手段
        ...
    else:
        # 如果未检测到异常的处理手段
        ...
    finally:
        # 无论是否检测到异常,都执行该代码块
        ...
    

      案例就不作过多说明了,这里说另外一个东西,触发异常和自定义异常

    触发异常和自定义异常

      python允许程序员自定义异常,用于描述python中没有涉及的异常情况,自定义异常必须继承Exception类,自定义异常按照命名规范以"Error"结尾,显式地告诉程序员这是异常。自定义异常使用raise语句引发,而且只能通过人工方式触发。当程序出现错误,python会自动引发异常,也可以通过raise显示地引发异常。一旦执行了raise语句,raise后面的语句将不能执行。

    # 触发自定义异常
    class InputError(Exception):
        def __init__(self):
            print("输入类型错误")
    
    inp = input('请输入一个数字:').strip()
    if inp.isdigit():
        raise InputError
    else:
        print(123)
    
    如果输入123
    则会报错。
    测试如下:
    请输入一个数字:123
    输入类型错误
    Traceback (most recent call last):
      File "E:/学习经历/python勃起/s2.py", line 90, in <module>
        raise InputError
    __main__.InputError
    

      

    # 捕获自定义异常
    class InputError(Exception):
    
    
    
    

      

    断言 assert

      assert语句用于检测某个条件表达式是否为真,如果检测到是假,则触发AssertionError

    ip = '127.0.1.1'
    assert ip == '127.0.0.1'
    
    out:
    Traceback (most recent call last):
      File "E:/学习经历/s2.py", line 109, in <module>
        assert ip == '127.0.0.1'
    AssertionError
    

      如果要为assert增加异常说明信息,语法:assert Expression [, message]

    ip = '127.0.1.1'
    assert ip == '127.0.0.1', 'No network connect, please the network connect again!'
    out: 
    Traceback (most recent call last):
      File "E:/学习经历/python勃起/s2.py", line 109, in <module>
        assert ip == '127.0.0.1', 'No network connect, please the network connect again!'
    AssertionError: No network connect, please the network connect again!
    

      

  • 相关阅读:
    【P2236】彩票(搜索+剪枝)
    【P1714】切蛋糕(单调队列)
    【P1886】滑动窗口(单调队列→线段树→LCT)
    【P3522】TEM(单调队列+DP)
    【P3957】跳房子(单调队列+DP+二分)
    【P1947】笨笨当粉刷匠(DP+前缀和)
    【P2564】生日礼物(单调队列)
    【[NOI2011]智能车比赛】(建图+spfa+坑爹精度)
    diyiti.cpp
    由最小环问题想到的
  • 原文地址:https://www.cnblogs.com/jishuweiwang/p/5618386.html
Copyright © 2011-2022 走看看