Python

Python学习(五)

2017-02-16  本文已影响46人  BlainPeng

类与对象

# 类的声明,括号中表示继承自哪个类,没有的话一般都是继承自object
class Student(object):
    # 类属性
    age = 18
    # 类似于Java中的构造方法, 给属性一些初始化值
    def __init__(self, name, score, sex):
        # 实例属性
        self.name = name
        self.score = score
        # 被双下划线"__"修饰的都是private属性,不能被外部访问
        self.__sex = sex

    """
    类中的方法,与普通函数的区别:第一个参数永远是self,表示创建的实例本身;
    外部调用方法时不需要传self
    """

    def print_info(self):
        print("%s,%d,%s" % (self.name, self.score, self.__sex))

    """
    提供set、get方法, 用于外部使用者对属性进行修改和获取
    """

    def set_sex(self, sex):
        self.__sex = sex

    def get_sex(self):
        return self.__sex


# 创建对象,并传入类属性初始化值
s = Student("Blain", 90, "male")
s.print_info()          # Blain,90,male
s.score = 95
s.print_info()          # Blain,95,male
s.set_sex("female")
print(s.get_sex())      # female
# 获取对象信息
print(hasattr(s, "name"))           # True
# 判断是否有属性
print(hasattr(s, "grade"))          # False
# 设置属性
setattr(s, "grade", 9)
print(hasattr(s, "grade"))          # True
# 没有这个属性会报错
# print(getattr(s, "address"))
# 无属性时也可以自定义返回结果
print(getattr(s, "address", "没有这个属性"))      # 没有这个属性
# 获取方法信息
fn = getattr(s, "print_info")
print(fn)           # <bound method Student.print_info of <__main__.Student object at 0x100686320>>
fn()                # Blain,95,female
# 类属性即可以用类来获取,也可以通过实例来获取,但实例属性的优先级比类属性要高
print(Student.age)  # 18
print(s.age)        # 18

面向对象高级编程

使用slots

slots定义的属性仅对当前类实例起作用,对继承的子类是不起作用

使用@property

修改和获取属性,可以通过实例对象直接获取属性来进行修改,但若这个属性是private的话就没办法修改;然后也可以通过类对外提供的set和get方法来获取属性或修改,不过这样的话略显繁琐。有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?之前有学到装饰器(decorator)可以给函数动态加上功能,Python内置的@property装饰器就是负责把一个方法变成属性调用的

class Student(object):
    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError("score must be integer")
        if value < 0 or value > 100:
            raise ValueError("score must be between 0 ~ 100")
        self.__score = value


s = Student()
s.score = 30            # 这里实际转化为s.set_score(60)
print(s.score)          # 30 这里实际转化为s.get_score()
s.score = 1000  
print(s.score)          # ValueError: score must be between 0 ~ 100

多重继承

一个类是可以继承另一个类的,但以前都是单继承,若需要“混入”额外的功能,通过多重继承就可以实现。在Python中,这种设计通常称之为MixIn

class Animal(object):
pass


class Mamal(Animal):
    pass


class Bird(Animal):
    pass


class RunnableMixIn(object):
    def run(self):
        print("可以跑的动物")


class FlyableMixIn(object):
    def fly(self):
        print("可以飞的动物")


class Dog(Mamal, RunnableMixIn):
    pass

# 很显然,鹦鹉既属于鸟类,也具有飞的功能
class Parrot(Bird, FlyableMixIn):
    pass

但在Java中,一个类只能够进行单继承,所以MixIn这种设计不适用Java。

定制类

class Student(object):
    def __init__(self, name):
        self.name = name
        self.a, self.b = 0, 1  # 初始化两个计数器a,b

    # 自定义类输入信息,有点类似于java中的toString方法, 返回用户看到的字符串
    def __str__(self):
        return "Student object (name: %s)" % self.name

    # 当在命令行交互模式下直接调用实例变量时,会调用这个方法,返回程序开发者看到的字符串;代码一般与__str__一致
    __repr__ = __str__

    # 如果一个类想被用于for ... in循环,就必须实现__iter__方法
    def __iter__(self):
        return self  # 实例本身就是迭代对象,故返回自己

    # 然后for循环就会不断调用该迭代对象的__next__方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > 20:
            raise StopIteration
        return self.a

    # 实现此方法能够使Student像list一样通过索引获取值
    def __getitem__(self, item):
        a, b = 1, 1
        if isinstance(item, int):  # item代表的是索引

            for x in range(item):
                a, b = b, a + b
            return a
        if isinstance(item, slice):  # item代表的是切片
            start = item.start
            stop = item.stop
            if start is None:
                start = 0
            l = []
            for x in range(stop):
                if x >= start:
                    l.append(a)
                a, b = b, a + b
            return l

    # 当调用不存在的属性时,Python解释器会试图调用此方法来获得属性
    # 这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。
    def __getattr__(self, attr):
        if attr == "sex":
            return "Male"
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

    # 实现此方法能够让对象像函数那样直接调用,也就是把对象看成函数,把函数看成对象,它们都是在运行期间创建出来的
    # 这样一来,我们就模糊了对象和函数数的界面,那如何来判断一个变量是对象还是函数呢? 可能通callable函数
    def __call__(self):
        print("My name is %s" % self.name)


# print(Student("Blain"))           # Student object (name: Blain)
# for n in Student("Blain"):
#     print(n)                      # 1,1,2,3,5,8,13

s = Student("Blain")
# print(s[0])                           # 1
# print(s[1])                           # 1
# print(s[2])                           # 2
# print(s[3])                           # 3
# print(s[4])                           # 5
# print(s[:5])                          # [1,1,2,3,5]
# print(s.age)                          # AttributeError: 'Student' object has no attribute 'age'
print(callable(s))                      # True
print(callable("test"))                 # False

使用枚举类

Enum可以把一组相关常量定义在一个class中,且class不变,而且成员也可以直接比较

from enum import Enum, unique

Month = Enum("Month", ("Jan", "Feb", "Mar", "Apr", "May", "June", "July"))
# 单独的引用一个常量
print(Month.Jan)            # Month.Jan
# 枚举它的所有成员
for name, member in Month.__members__.items():
    # value属性是自动赋给成员的int常量,默认是从1开始
    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
    # June => Month.June , 6
    # July => Month.July , 7

# 自定义枚举类
@unique  # @unique装饰器可以帮助我们检查保证没有重复值
class Weekday(Enum):
    Sun = 0  # Sun的Value被设置为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6


d1 = Weekday.Mon
print(d1)                        # Weekday.Mon
print(Weekday.Mon)              # Weekday.Mon
print(Weekday["Mon"])             # Weekday.Mon
print(Weekday(1))               # Weekday.Mon
print(Weekday.Mon.value)          # 1
print(d1 == Weekday(1))       # True
print(d1 == Weekday.Mon)        # True
上一篇下一篇

猜你喜欢

热点阅读