zoukankan      html  css  js  c++  java
  • Python基础学习(六)

    前几天一直在练手廖雪峰老师的python课程,接下来继续学习,由于面向对象编程这一课相对理论便不在此练手,直接上手面向对象高级编程。

    一、使用 __slots__

      一般情况下一个class是可以绑定一个属性和方法的,例如:

    #给实例绑定属性 和 方法
    #绑定属性
    class Student(object):
        pass
    
    s = Student()
    s.name = 'nihao'
    print(s.name)
    

      绑定方法:

    #绑定方法
    def setAge(self, a):
        self.age = a
    
    from types import MethodType
    s.setAge = MethodType(setAge, s)
    s.setAge(22)
    print( s.age )
    

      给一个实例绑定一个方法,对于另外一个实例是不起作用的

      例如:

    #给一个实例绑定一个方法,对于另外一个实例是不起作用的
    s2 = Student()
    #s2.setAge(11)
    

      说明:这里的s2.setAge(11)是会报错的。

      但是给class绑定方法后,所有实例均可调用

    #但是给class绑定方法后,所有实例均可调用
    def setScore(self, score):
        self.score = score
    
    Student.setScore = setScore
    
    s.setScore(11)
    print( s.score )
    
    s2.setScore(33)
    print( s2.score )
    

      其他的案例将函数写在class里面,任何实例都可以调用:

    class Test(object):
        def myFun(self, num):
            self.num = num
    
    t = Test()
    t.myFun(22)
    print(t.num)
    

      总结:可以给一个实例绑定方法和属性,但是另外一个实例调用是不起作用的,除非给class绑定一个方法,或者在class里面自定义一个方法,这样才会在所有的实例中都可以调用。

      使用__slots__

      说明:__slots__ 的英文单词是槽的意思,也就是一个位置的意思。占用一个坑。__slots__的好处是可以限定一个class的实例绑定属性的个数和指定属性的标准。

           比方说我限定了一个类只能在外部绑定哪些属性名称,通过__slots__定义的就可以使用 否则定义其他属性的 class 都不认识。

      案例:

    class Student(object):
        __slots__ = ('name', 'age') #用tuple定义允许绑定的属性名称
    
    s = Student()
    s.name = 'cici'
    print( s.name )
    s.age = '18'
    print( s.age )
    s.score = 100
    print( s.score )
    

      输出:

    cici
    18
    Traceback (most recent call last):
      File "/mnt/hgfs/webspace/pythonStudy/one.py", line 678, in <module>
        s.score = 100
    AttributeError: 'Student' object has no attribute 'score'
    

      由此可见,只有定义了属性名称,是可以做外部绑定的,没有定义的属性名称,是绑定不成功的。

      另外需要注意的是:__slots__只对当前类起作用,对于继承的子类是不起作用的。

    二、使用@property

      通常我们通过实例去给一个实例绑定一个属性,或者修改属性的值,显然很不安全,可以通过如下案例实现:

    class Student(object):
    
        def getScore(self):
            return self._score
    
        def setScore(self, val):
            if not isinstance(val, int):
                raise ValueError('请输入整型')
            if val <0 or val> 100:
                raise ValueError('范围要在0-100直间')
            self._score = val
    
    
    s = Student()
    
    s.setScore(44)
    print( s.getScore() )
    

      说明:由此可见,通过设置方法,去设置参数,再通过另外一个方法去获取参数。明显比较安全些,但是这种写法稍微复杂了一点,要调用两个方法才行

      python恰好有一个即可以验证参数,有可以像普通绑定属性这样简单的方式,那就是python内置的@property装饰器 装饰器就是负责把一个方法变成属性调用的形式:
      案例:

    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, val):
            if not isinstance(val, int):
                raise ValueError('score is integer')
            elif val <0 or val >100:
                raise ValueError('0-100')
            self._score = val
    
    
    s = Student()
    s.score = 90
    
    print(s.score)
    

      输出:90

      说明:要将一个getter方法变成属性一样的调用,只需要加上@property就可以了,另外 @property 本身又创建了一个装饰器@score.setter 负责把一个setter方法变成属性赋值,这样就可以得到一个可控的属性操作了。

    三、定制类

      在python中类似__xxx__的变量或则函数名说明它们是拥有特殊用途的函数。

      例如:

      __str__

      案例:

    class Student(object):
        def __init__(self, name):
            self.name = name
    
    print( Student('cici') )
    

      输出:<__main__.Student object at 0x7fd703484940>

      说明:由此可见,这种输出是非常不好看的,改一下

    class Student(object):
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return 'Student object (name: %s)' % self.name
    
    print( Student('cici') )
    

      输出:Student object (name: cici)

      由此可见,__str__()方法是可以返回一个好看的字符串的,而且还可以看出实例内部的数据。

      __iter__ 

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 #初始化两个计数器
    
        def __iter__(self):
            return  self #实例本身就是迭代对象
    
        def __next__(self):
            self.a, self.b = self.b,  self.a+self.b #计算下一个值
            if self.a > 20: #退出循环的条件
                raise StopIteration()
            return self.a #返回下一个值
    
    for item in Fib():
        print(item)
    

      输出:

    1
    1
    2
    3
    5
    8
    13
    

      说明:如果一个类想要被for ... in ... 循环,类似list 或 tuple ,就必须实现一个__iter__() 方法 返回它自己,改方法返回一个迭代对象,然后,python 的 for 循环将会不间断的调用该迭代对象的 __next__() 方法,拿到循环的下一个值,直到遇到 StopIteration 错误时退出。

         __getitem__

      上面定义的Fib实例虽然可以作用于for循环,看起来类似list,但是它并不能类似list一样去使用,比如取值的时候:  

    class Fib(object):
        def __getitem__(self, item):
            a,b = 1,1
            for x in range(item):
                a, b = b, a+b
            return a
    
    print(Fib()[0])
    print(Fib()[1])
    print(Fib()[2])
    print(Fib()[3])
    print(Fib()[4])
    print(Fib()[5])
    

      输出:

    1
    1
    2
    3
    5
    8
    

      说明:由此可见,若要将一个实例像list那样调用按照下标取元素,需要在定义一个__getitem__()方法。

      注意: list 有一个切片方法,但是用在类里面会报错,这是由于__getitem__()传入的参数有可能是一个int 也有可能是一个切片的对象slice,所以需要做判断:

      __getattr__

      一般情况下,如果我们调用一个类的方法或者属性,如果不存在,就会报错,例如:

    class Student(object):
    
        def __init__(self):
            self.name = 'cici'
    
    s = Student()
    print( s.name )
    print( s.score )
    

      输出:

    cici
    Traceback (most recent call last):
      File "/mnt/hgfs/webspace/pythonStudy/one.py", line 771, in <module>
        print( s.score )
    AttributeError: 'Student' object has no attribute 'score'
    

      说明:由此可见,调用 name 的时候没有问题,而调用 score的时候说Student没有找score的元素

      通过__getattr__控制

    class Student(object):
    
        def __init__(self):
            self.name = 'cici'
    
        def __getattr__(self, item):
            if item == 'score':
                return 100
    
    s = Student()
    print( s.name )
    print( s.score )
    print( s.aaaaa )
    

      输出:

    cici
    100
    None
    

      说明:由此可见,在类中增加了一个__getattr__ 可以自定义输出的属性,另外在次访问不存在的属性或时会友好的返回None

      __call__

      一个对象实例可以有自己的属性和方法,当我们调用实例方法时使用 instance.method() 来调用,能不能直接在实例本身上调用呢?案例

      例如:

    class Student(object):
        def __init__(self, name):
            self.name = name
    
        def __call__(self):
            return self.name
    
    s = Student('cici')
    print( s() )
    

      输出:‘cici'

      说明:由此可见,如果像调用实例本身 s() 其实是可以调用成功的,因为这类中我们已经定义了一个__call__()方法,这样就实现了调用实例就好像调用函数一样的效果。

         那么问题就来了,如果区分它是一个对象,还是一个函数呢?更多时候,我们需要判断一个对象是否能够被调用,能够被调用的对象就是一个Callable对象

      例如:  

    print( callable(Student) )
    

      返回:True

      所以、通过callable()函数,就可以判断这个对象是否是 可调用的 对象。

    四、使用枚举类

      定义一个枚举

    from enum import Enum, unique
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    
    for name, member in Month.__members__.items():
        print(name, '=>', member, ',', member.value)
    

      输出:

    Jan => Month.Jan , 1
    Feb => Month.Feb , 2
    Mar => Month.Mar , 3
    Apr => Month.Apr , 4
    May => Month.May , 5
    Jun => Month.Jun , 6
    Jul => Month.Jul , 7
    Aug => Month.Aug , 8
    Sep => Month.Sep , 9
    Oct => Month.Oct , 10
    Nov => Month.Nov , 11
    Dec => Month.Dec , 12
    

      访问枚举的方法

    @unique
    class Weekday(Enum):
        Sun = 0  # Sun的value被设定为0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    
    #访问这些枚举类型的方法
    day1 = Weekday.Mon
    print( day1 )
    print( day1.value )
    print( Weekday['Thu'])
    print( Weekday['Thu'].value)
    print( Weekday(2) )
    

      输出:

    Weekday.Mon
    1
    Weekday.Thu
    4
    Weekday.Tue
    

      可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

    五、使用元类

      。。。

       

      

  • 相关阅读:
    HDU 5583 Kingdom of Black and White 水题
    HDU 5578 Friendship of Frog 水题
    Codeforces Round #190 (Div. 2) E. Ciel the Commander 点分治
    hdu 5594 ZYB's Prime 最大流
    hdu 5593 ZYB's Tree 树形dp
    hdu 5592 ZYB's Game 树状数组
    hdu 5591 ZYB's Game 博弈论
    HDU 5590 ZYB's Biology 水题
    cdoj 1256 昊昊爱运动 预处理/前缀和
    cdoj 1255 斓少摘苹果 贪心
  • 原文地址:https://www.cnblogs.com/dump/p/9592989.html
Copyright © 2011-2022 走看看