Python学习(五)

类与对象

# 类的声明,括号中表示继承自哪个类,没有的话一般都是继承自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

  • 给一个类动态的绑定属性和方法

      from types import MethodType
    
      class People(object):
          pass
      
      p1 = People()
      p2 = People()
      # 动态的给实例p1绑定name属性
      p1.name = "Blain"
      
      # 定义一个函数作为实例方法
      def set_age(self, age):
          self.age = age
      
      # 动态的给实例p1绑定一个方法
      p1.set_age = MethodType(set_age, p1)
      
      print(p1.name)          # Blain
      p1.set_age(18)
      print(p1.age)           # 18
      
      # 给一个实例绑定的方法,对另一个实例是不起作用的
      # p2.set_age(19)  # AttributeError: 'People' object has no attribute 'set_age'
      
      # 为了给所有实例都绑定方法,可以给class绑定方法:
      def set_score(self, score):
          self.score = score
      
      People.set_score = set_score
      p1.set_score(80)
      print(p1.score)         # 80
      p2.set_score(90)
      print(p2.score)         # 90
    
  • 限制实例的属性

      class People2(object):
          # 用tuple定义允许绑定的属性名称
          __slots__ = ("name", "age")
      
      p3 = People2()
      p3.name = "Tom"
      p3.age = 16
      # p3.score = 99  # AttributeError: 'People2' object has no attribute 'score'
    

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • 要点: 函数式编程:注意不是“函数编程”,多了一个“式” 模块:如何使用模块 面向对象编程:面向对象的概念、属性、...
    victorsungo阅读 1,461评论 0 6
  • 定义类并创建实例 在Python中,类通过 class 关键字定义。以 Person 为例,定义一个Person类...
    绩重KF阅读 3,924评论 0 13
  • Python进阶框架 希望大家喜欢,点赞哦首先感谢廖雪峰老师对于该课程的讲解 一、函数式编程 1.1 函数式编程简...
    Gaolex阅读 5,489评论 6 53
  • 前两天晚上都在加班,今天很幸运,没加太久。吃过晚饭和同事出去散步一个多小时,回来洗好澡又洗衣服的,又晚上10点了,...
    事是拾阅读 320评论 2 8