DataWhale7月学习——Python入门

Task7:类、对象与魔法方法

本节我们初步学习Python语法中的类、对象和魔法方法。文章给出了一些重点知识的.py程序便于读者深入理解。本文的程序编写基于Python3.0+,安装环境使用的是PyCharm。

类与对象

对象=属性+方法

对象是类的实例。
类主要定义对象的结构,以类为模板创建对象。类不但包含方法定义,还包含所有实例共享的数据。
1.封装
利用关键字class定义Python中的类,关键字后面紧跟类的名称、分号和类的实现

class Turtle: # Python中的类名约定以⼤写字⺟开头
    """关于类的⼀个简单例⼦"""
     # 属性
    color = 'green'
    weight = 10
    legs = 4
    shell = True
    mouth = '⼤嘴'
    # ⽅法
    def climb(self):
       print('我正在很努⼒的向前爬...')
    def run(self):
       print('我正在⻜快的向前跑...')
    def bite(self):
       print('咬死你咬死你!!')
    def eat(self):
       print('有得吃,真满⾜...')
    def sleep(self):
       print('困了,睡了,晚安,zzz')
tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>
print(type(tt))
# <class '__main__.Turtle'>
print(tt.__class__)
# <class '__main__.Turtle'>
print(tt.__class__.__name__)
# Turtle
tt.climb()
# 我正在很努⼒的向前爬...
tt.run()
# 我正在⻜快的向前跑...
tt.bite()
# 咬死你咬死你!!
# Python类也是对象。它们是type的实例
print(type(Turtle))
# <class 'type'>

2.继承
子类自动共享父类之间的数据和方法机制

class MyList(list):
     pass
lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)
# [1, 2, 5, 7, 8, 9]

3.多态
不同对象对同一方法响应不同的行动

class Animal:
   def run(self):
      raise AttributeError('⼦类必须实现这个⽅法')
class People(Animal):
   def run(self):
      print('⼈正在⾛')
class Pig(Animal):
   def run(self):
      print('pig is walking')
class Dog(Animal):
   def run(self):
      print('dog is running')
def func(animal):
   animal.run()
func(Pig())
# pig is walking

类方法与普通函数只有一个特别的区别——他们必须有一个额外的第一个参数名称(self),在调用时,无需提供与参数self相对应的参数

class Ball:
   def setName(self, name):
      self.name = name
   def kick(self):
      print("我叫%s,该死的,谁踢我..." % self.name) 
a = Ball()
a.setName("球A") 
b = Ball()
b.setName("球B") 
c = Ball()
c.setName("球C") 
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...

Python的魔法方法

Python中类有一个名为__init__(self[, param1, param2…])的魔法方法,该方法在类实例化时会自动调用

class Ball:
    def __init__(self, name):
        self.name = name
    def kick(self):
        print("我叫%s,该死的,谁踢我..." % self.name) 
a = Ball("球A") 
b = Ball("球B") 
c = Ball("球C") 
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...

公有和私有

在Python中定义私有变量只需要在变量名或函数名前加上两个下划线,那么这个函数或变量就会私有了
1.类的私有属性

class JustCounter:
     __secretCount = 0 # 私有变量
     publicCount = 0 # 公开变量
     def count(self):
         self.__secretCount += 1
         self.publicCount += 1
         print(self.__secretCount)
counter = JustCounter()
counter.count() # 1
counter.count() # 2
print(counter.publicCount) # 2
print(counter._JustCounter__secretCount) # 2 Python的私有为伪私有
print(counter.__secretCount) 
# AttributeError: 'JustCounter' object has no attribute '__secretCount'

2.类的私有方法

class Site:
    def __init__(self, name, url):
       self.name = name # public
       self.__url = url # private
    def who(self):
        print('name : ', self.name)
        print('url : ', self.__url)
    def __foo(self): # 私有⽅法
        print('这是私有⽅法')
    def foo(self): # 公共⽅法
        print('这是公共⽅法')
        self.__foo()
x = Site('⽼⻢的程序⼈⽣', 'https://blog.csdn.net/LSGO_MYP') 
x.who()
# name : ⽼⻢的程序⼈⽣
# url : https://blog.csdn.net/LSGO_MYP
x.foo()
# 这是公共⽅法
# 这是私有⽅法
x.__foo()
# AttributeError: 'Site' object has no attribute '__foo'

继承

派生类的定义:

class DerivedClassName(BaseClassName):
     <statement-1>
          .
          .
          .
     <statement-N>

基类名BaseClassName必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模板

class DerivedClassName(modname.BaseClassName):
     <statement-1>
          .
          .
          .
     <statement-N>

如果子类中定义与父类同名的方法或属性,则会覆盖父类对应的方法或属性

# 类定义
class people:
    # 定义基本属性
    name = ''
    age = 0
    # 定义私有属性,私有属性在类外部⽆法直接进⾏访问
    __weight = 0
    # 定义构造⽅法
    def __init__(self, n, a, w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 单继承示例
class student(people):
       grade = ''
       def __init__(self, n, a, w, g):
           # 调⽤⽗类的构函
           people.__init__(self, n, a, w)
           self.grade = g
        # 覆写⽗类的⽅法
       def speak(self):
           print("%s 说: 我 %d 岁了,我在读 %d 年级" % (self.name, self.age, self.grade))
s = student('⼩⻢的程序⼈⽣', 10, 60, 3) 
s.speak()
# ⼩马的程序⼈⽣ 说: 我 10 岁了,我在读 3 年级
class Fish:
     def __init__(self):
         self.x = r.randint(0, 10)
         self.y = r.randint(0, 10)
     def move(self):
         self.x -= 1
         print("我的位置", self.x, self.y)
class GoldFish(Fish): # ⾦⻥
    pass
class Carp(Fish): # 鲤⻥
    pass
class Salmon(Fish): # 三⽂⻥
    pass
class Shark(Fish): # 鲨⻥
     def __init__(self):
         self.hungry = True
     def eat(self):
          if self.hungry:
                print("吃货的梦想就是天天有得吃!")
                self.hungry = False
          else:
                print("太撑了,吃不下了!")
                self.hungry = True
g = GoldFish()
g.move() # 我的位置 9 4
s = Shark()
s.eat() # 吃货的梦想就是天天有得吃!
s.move() 
# AttributeError: 'Shark' object has no attribute 'x'

调用未绑定的父类方法Fish.init(self)

class Shark(Fish): # 鲨⻥
     def __init__(self):
         Fish.__init__(self)
         self.hungry = True
     def eat(self):
          if self.hungry:
              print("吃货的梦想就是天天有得吃!")
              self.hungry = False
          else:
              print("太撑了,吃不下了!")
              self.hungry = True

使用super函数super().init()

class Shark(Fish): # 鲨⻥
   def __init__(self):
       super().__init__()
       self.hungry = True
   def eat(self):
       if self.hungry:
           print("吃货的梦想就是天天有得吃!")
           self.hungry = False
       else:
           print("太撑了,吃不下了!")
           self.hungry = True

多继承
注意圆括号中父类的顺序,若父类有相同的类名,而在子类使用时未指定,Python从左到右搜索,即在子类中未找到时,从左到右查找父类中是否包含方法

# 类定义
class People:
   # 定义基本属性
   name = ''
   age = 0
   # 定义私有属性,私有属性在类外部⽆法直接进⾏访问
    __weight = 0
    # 定义构造⽅法
    def __init__(self, n, a, w):
       self.name = n
       self.age = a
       self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 单继承示例
class Student(People):
    grade = ''
    def __init__(self, n, a, w, g):
        # 调⽤⽗类的构函
       People.__init__(self, n, a, w)
       self.grade = g
    # 覆写⽗类的⽅法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级" % (self.name, self.age, self.grade))
# 另⼀个类,多重继承之前的准备
class Speaker:
    topic = ''
    name = ''
    def __init__(self, n, t):
       self.name = n
       self.topic = t
    def speak(self):
        print("我叫 %s,我是⼀个演说家,我演讲的主题是 %s" % (self.name, self.topic))
# 多重继承
class Sample01(Speaker, Student):
    a = ''
    def __init__(self, n, a, w, g, t):
       Student.__init__(self, n, a, w, g)
       Speaker.__init__(self, n, t)

test = Sample01("Tim", 25, 80, 4, "Python")
test.speak() # ⽅法名同,默认调⽤的是在括号中排前地⽗类的⽅法
# 我叫 Tim,我是⼀个演说家,我演讲的主题是 Python
class Sample02(Student, Speaker):
      a = ''
      def __init__(self, n, a, w, g, t):
         Student.__init__(self, n, a, w, g)
         Speaker.__init__(self, n, t)
test = Sample02("Tim", 25, 80, 4, "Python")
test.speak() # ⽅法名同,默认调⽤的是在括号中排前地⽗类的⽅法
# Tim 说: 我 25 岁了,我在读 4 年级

组合

class Turtle:
   def __init__(self, x):
      self.num = x
class Fish:
    def __init__(self, x):
      self.num = x
class Pool:
    def __init__(self, x, y):
       self.turtle = Turtle(x)
       self.fish = Fish(y)
    def print_num(self):
       print("⽔池⾥⾯有乌⻳%s只,⼩⻥%s条" % (self.turtle.num, self.fish.num))
p = Pool(2, 3)
p.print_num()
# ⽔池⾥⾯有乌⻳2只,⼩⻥3条

类、类对象和实例对象

类对象:创建一个类,其实也是一个对象在内存开辟了一块空间,类对象只有一个
实例对象:实例化类创建的对象,实例对象可以有多个
类属性:类里面方法外面定义的变量,类属性所属类对象并且对个实例对象之间公用一个类属性
实例属性:实例属性和具体的某个实例对象有关系,并且一个实例对象和另外一个实例对象不公用属性
注意:属性与方法名相同时,属性会覆盖方法

# 类对象
class A(object):
    pass
# 实例化对象 a、b、c都属于实例对象。
a = A()
b = A()
c = A()
class A():
    a = xx #类属性
    def __init__(self):
        A.a = xx #使⽤类属性可以通过 (类名.类属性)调⽤
class 类名():
    __init__(self):
       self.name = xx #实例属性

类属性和实例属性区别:
1.类属性:类外面,可以通过实例对象.类属性和类名.类属性进行调用;类里面,可以通过self.类属性和类名.类属性进行调用
2.实例对象:类外面,可以通过实例对象.实例属性调用;类里面self.实例属性调用
3.实例对象相当于局部变量,出了这个类或者类的实例对象,就没有作用了;类对象相当于全局变量,可以和这个类的所有实例对象共享

# 创建类对象
class Test(object):
    class_attr = 100 # 类属性
    def __init__(self):
       self.sl_attr = 100 # 实例属性
    def func(self):
       print('类对象.类属性的值:', Test.class_attr) # 调⽤类属性
       print('self.类属性的值', self.class_attr) # 相当于把类属性 变成实例属性
       print('self.实例属性的值', self.sl_attr) # 调⽤实例属性
a = Test()
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
b = Test()
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
a.class_attr = 200
a.sl_attr = 200
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
Test.class_attr = 300
a.func()
# 类对象.类属性的值: 300
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 300
# self.类属性的值 300
# self.实例属性的值 100
class A:
   def x(self):
      print('x_man')
aa = A()
aa.x() # x_man
aa.x = 1
print(aa.x) # 1
aa.x()
# TypeError: 'int' object is not callable

绑定

Python严格要求方法必须有实例才能被调用。Python对象的数据属性通常存储在名为.__ dict__的字典中,我们直接访问__dict__或者利用Python中的内置函数vars()获取.__ dict__

class CC:
   def setXY(self, x, y):
      self.x = x
      self.y = y
   def printXY(self):
      print(self.x, self.y)
dd = CC()
print(dd.__dict__)
# {}
print(vars(dd))
# {}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY':
<function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
dd.setXY(4, 5)
print(dd.__dict__)
# {'x': 4, 'y': 5}
print(vars(CC))
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY':
<function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY':
<function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}

一些内置函数

1.issubclass(class, classinfo)用于判断参数class是否是类型参数classinfo的子类;
classinfo可以是类对象的元组,只要class是其中任何一个候选类的子类,则返回True

class A:
   pass
class B(A):
   pass
print(issubclass(B, A)) # True
print(issubclass(B, B)) # True
print(issubclass(A, B)) # False
print(issubclass(B, object)) # True

2.isinstance(object, classinfo)用于判断一个对象是否是一个已知类型,类似于type();type()不会认为子类是一种父类类型,不考虑继承关系;isinstance()会认为子类是一种父类类型,考虑继承关系。
如果第一个参数不是对象,则返回False;如果第二个参数不是类或者由类对象组成的元组,则返回
TypeError异常

a = 2
print(isinstance(a, int)) # True
print(isinstance(a, str)) # False
print(isinstance(a, (str, int, list))) # True
class A:
    pass
class B(A):
    pass
print(isinstance(A(), A)) # True
print(type(A()) == A) # True
print(isinstance(B(), A)) # True
print(type(B()) == A) # False

3.hasattr(object, name)用于判断对象是否包含对应的属性

class Coordinate:
    x = 10
    y = -5
    z = 0
point1 = Coordinate()
print(hasattr(point1, 'x')) # True
print(hasattr(point1, 'y')) # True
print(hasattr(point1, 'z')) # True
print(hasattr(point1, 'no')) # False

4.getattr(object, name[, default])用于返回一个对象属性值

class A(object):
    bar = 1 
a = A()
print(getattr(a, 'bar')) # 1
print(getattr(a, 'bar2', 3)) # 3
print(getattr(a, 'bar2'))
# AttributeError: 'A' object has no attribute 'bar2'
class A(object):
   def set(self, a, b):
       x = a
       a = b
       b = x
       print(a, b) 
a = A()
c = getattr(a, 'set') 
c(a='1', b='2') # 2 1

5.setattr(object, name, value)对应函数getattr(),用于设置属性值,该属性不一定存在

class A(object):
    bar = 1 
a = A()
print(getattr(a, 'bar')) # 1
setattr(a, 'bar', 5)
print(a.bar) # 5
setattr(a, "age", 28)
print(a.age) # 28

6.delattr(object, name)用于删除属性

class Coordinate:
    x = 10
    y = -5
    z = 0
point1 = Coordinate()
print('x = ', point1.x) # x = 10
print('y = ', point1.y) # y = -5
print('z = ', point1.z) # z = 0
delattr(Coordinate, 'z')
print('--删除 z 属性后--') # --删除 z 属性后--
print('x = ', point1.x) # x = 10
print('y = ', point1.y) # y = -5
# 触发错误
print('z = ', point1.z)
# AttributeError: 'Coordinate' object has no attribute 'z'

7.class property([fget[, fset[, fdel[, doc]]]])用于在新类中返回属性值

class C(object):
   def __init__(self):
        self.__x = None
   def getx(self):
        return self.__x
   def setx(self, value):
        self.__x = value
   def delx(self):
        del self.__x
    x = property(getx, setx, delx, "I'm the 'x' property.")
cc = C()
cc.x = 2
print(cc.x) # 2

魔法方法

魔法方法总是被双下划线包围:init
魔法方法总是面向Python的一切,总能够在适当的时候被自动调用
魔法方法的第一个参数应该为cls(类方法)或者self(实例方法)

基本的魔法方法

1.init(self[, …])
构造器,当一个实例被创建的时候调用的初始化方法

class Rectangle:
    def __init__(self, x, y):
       self.x = x
       self.y = y
    def getPeri(self):
       return (self.x + self.y) * 2
    def getArea(self):
       return self.x * self.y
rect = Rectangle(4, 5)
print(rect.getPeri()) # 18
print(rect.getArea()) # 20

2.new(cls[, …])
new__是在一个对象实例化的时候调用的第一个方法,在调用__init__初始化前,先调用__new
new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,后面的参数传递给__init
new__对当前类进行了实例化,并将实例返回,传给__init__的self。但是执行了__new,并不一定会进入__init__,只有__new__返回了,当前cls的实例,当前类的__init__才进入

class A(object):
   def __init__(self, value):
      print("into A __init__")
      self.value = value
   def __new__(cls, *args, **kwargs):
      print("into A __new__")
      print(cls)
      return object.__new__(cls)
class B(A):
   def __init__(self, value):
      print("into B __init__")
      self.value = value
   def __new__(cls, *args, **kwargs):
      print("into B __new__")
      print(cls)
      return super().__new__(cls, *args, **kwargs) 
b = B(10)
# 结果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.B'>
# into B __init__
class A(object):
   def __init__(self, value):
      print("into A __init__")
      self.value = value
   def __new__(cls, *args, **kwargs):
      print("into A __new__")
      print(cls)
      return object.__new__(cls)
class B(A):
   def __init__(self, value):
      print("into B __init__")
      self.value = value
   def __new__(cls, *args, **kwargs):
       print("into B __new__")
       print(cls)
       return super().__new__(A, *args, **kwargs) # 改动了cls变为A
b = B(10)
# 结果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>

若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行,将没有__init__被调用
可利用__new__实现单例模式

class Earth:
    pass
a = Earth()
print(id(a)) # 260728291456
b = Earth()
print(id(b)) # 260728291624
class Earth:
    __instance = None # 定义⼀个类属性做判断
    def __new__(cls):
    if cls.__instance is None:
       cls.__instance = object.__new__(cls)
       return cls.__instance
    else:
       return cls.__instance
a = Earth()
print(id(a)) # 512320401648
b = Earth()
print(id(b)) # 512320401648

__new__方法主要是当继承一些不可变的class时(如int, str, tuple),提供给你一个自定义这些类的实例化过程的途径

class CapStr(str):
   def __new__(cls, string):
      string = string.upper()
      return str.__new__(cls, string) 
a = CapStr("i love lsgogroup")
print(a) # I LOVE LSGOGROUP

3.del(self)
析构器,当一个对象将要被系统回收时调用的方法

class C(object):
   def __init__(self):
      print('into C __init__')
   def __del__(self):
      print('into C __del__')
c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__

4.str__和__repr
str(self):
当你打印一个对象时,触发__str__;当你使用%s格式的时候,触发__str__;str强制转换类型的时候,触发__str__
repr(self):
repr是str的备胎;有__str__的时候执行__str__,没有__str__的之后执行__repr__;repr(obj)内置函数对应的结果是__repr__的返回值;当你使用%r格式化的时候触发__repr__

class Cat:
 """定义⼀个猫类"""
    def __init__(self, new_name, new_age):
       """在创建完对象之后 会⾃动调⽤, 它完成对象的初始化的功能"""
      self.name = new_name
      self.age = new_age
    def __str__(self):
        """返回⼀个对象的描述信息"""
      return "名字是:%s , 年龄是:%d" % (self.name, self.age)
 
    def __repr__(self):
        """返回⼀个对象的描述信息"""
      return "Cat:(%s,%d)" % (self.name, self.age)
    def eat(self):
      print("%s在吃⻥...." % self.name)
    def drink(self):
      print("%s在喝可乐..." % self.name)
    def introduce(self):
      print("名字是:%s, 年龄是:%d" % (self.name, self.age))
# 创建了⼀个对象
tom = Cat("汤姆", 30)
print(tom) # 名字是:汤姆 , 年龄是:30
print(str(tom)) # 名字是:汤姆 , 年龄是:30
print(repr(tom)) # Cat:(汤姆,30)
tom.eat() # 汤姆在吃⻥....
tom.introduce() # 名字是:汤姆, 年龄是:30

str(self)的返回结果可读性强;repr(self)的返回结果更准确

import datetime
today = datetime.date.today()
print(str(today)) # 2019-10-11
print(repr(today)) # datetime.date(2019, 10, 11)
print('%s' %today) # 2019-10-11
print('%r' %today) # datetime.date(2019, 10, 11)

算术运算符

类型工厂函数:不通过类而是通过函数来创建对象

class C:
   pass
print(type(len)) # <class 'builtin_function_or_method'>
print(type(dir)) # <class 'builtin_function_or_method'>
print(type(int)) # <class 'type'>
print(type(list)) # <class 'type'>
print(type(tuple)) # <class 'type'>
print(type(C)) # <class 'type'>
print(int('123')) # 123
# 这个例⼦中list⼯⼚函数把⼀个元祖对象加⼯成了⼀个列表对象。
print(list((1, 2, 3))) # [1, 2, 3]

add(self, other)定义加法行为+
sub(self, other)定义减法行为-
mul(self, other)定义乘法行为*
truediv(self, other)定义真除法行为/
floordiv(self, other)定义整数除法行为//
mod(self, other)定义取模算法的行为%
divmod(self, other)定义当被divmod()调用时的行为
divmod(a, b)把除数和余数运算结合起来,返回一个包含商和余数的元组(a // b, a % b)
pow(self, other[, module])定义当被power()调用或**运算时的行为
lshift(self, other)定义按位左移位的行为:<<
rshift(self, other)定义按位右移位的行为:>>
and(self, other)定义按位与操作的行为:&
xor(self, other)定义按位异或操作的行为:^
or(self, other)定义按位或操作的行为:|

class MyClass:
    def __init__(self, height, weight):
       self.height = height
       self.weight = weight
 # 两个对象的⻓相加,宽不变.返回⼀个新的类
     def __add__(self, others):
        return MyClass(self.height + others.height, self.weight + others.weight)
 # 两个对象的宽相减,⻓不变.返回⼀个新的类
     def __sub__(self, others):
        return MyClass(self.height - others.height, self.weight - others.weight)
 # 说⼀下⾃⼰的参数
     def intro(self):
         print("⾼为", self.height, " 重为", self.weight)
     def main():
         a = MyClass(height=10, weight=5)
         a.intro()
         b = MyClass(height=20, weight=10)
         b.intro()
         c = b - a
         c.intro()
         d = a + b
         d.intro()
if __name__ == '__main__':
 main()
# ⾼为 10 重为 5
# ⾼为 20 重为 10
# ⾼为 10 重为 5
# ⾼为 30 重为 15
print(divmod(7, 2)) # (3, 1)
print(divmod(8, 2)) # (4, 0)

反算术运算符

与算数运算符不同的是多了个‘r’,当文件左操作不支持相应的操作时调用
radd(self, other)定义加法运算+
rsub(self, other)定义减法运算-
rmul(self, other)定义乘法运算*
rtruediv(self, other)定义真除法运算/
a + b 这里加数是a,被加数是b,因此是a主动,反运算就是如果a对象的__add__()没有实现或者不支持相应的操作,那么Python就会调用b的__radd__()方法

class Nint(int):
    def __radd__(self, other):
       return int.__sub__(other, self) # 注意 self 在后⾯
a = Nint(5) 
b = Nint(3)
print(a + b) # 8
print(1 + b) # -2

属性访问

getattrgetattributesetattr__和__delattr
getattr(self, name):定义当用户试图获取一个不存在的属性时的行为
getattribute(self, name):定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)
setattr(self, name, value):定义当一个属性被设置时的行为
delattr(self, name):定义当一个属性被删除时的行为

class C:
    def __getattribute__(self, item):
       print('__getattribute__')
       return super().__getattribute__(item)
    def __getattr__(self, item):
        print('__getattr__')
    def __setattr__(self, key, value):
        print('__setattr__')
        super().__setattr__(key, value)
    def __delattr__(self, item):
        print('__delattr__')
        super().__delattr__(item)
c = C()
c.x
# __getattribute__
# __getattr__
c.x = 1
# __setattr__
del c.x
# __delattr__

描述符

描述符就是将某种特殊类型的类的实例指派给另一个类的属性
get(self, instance, owner)用于访问属性,它返回属性的值
set(self, instance, value)将在属性分配操作中调用,不返回任何内容
del(self, instance)控制删除操作,不返回任何内容

class MyDecriptor:
    def __get__(self, instance, owner):
       print('__get__', self, instance, owner)
    def __set__(self, instance, value):
       print('__set__', self, instance, value)
    def __delete__(self, instance):
       print('__delete__', self, instance)
class Test:
   x = MyDecriptor()
t = Test()
t.x
# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at
0x000000CEABDC0898> <class '__main__.Test'>
t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at
0x00000023696B0940> x-man
del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at
0x000000EC9B160B38>

定制序列

容器类型的协议
如果希望定制的容器是不可变的,只需要定义__len__()和__getitem__()
如果希望定制的容易是可变的,还需要的定义__setitem__()和__delitem__()两个方法
例:编写一个不可变的自定义列表,要求记录列表中每个元素被访问的次数

class CountList:
   def __init__(self, *args):
      self.values = [x for x in args]
      self.count = {}.fromkeys(range(len(self.values)), 0)
   def __len__(self):
      return len(self.values)
   def __getitem__(self, item):
      self.count[item] += 1
      return self.values[item]
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
print(c1[1] + c2[1]) # 7
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 1, 2: 1, 3: 0, 4: 0}

len(self)定义当被len()调用时的行为(返回容器中元素的个数)
getitem(self, key)定义获取元素的行为,相当于self[key]
setitem(self, key, value)定义设置容器中指定元素的行为,相当于self[key] = value
delitem(self, key)定义删除容器中指定元素的行为,相当于del self[key]

例:编写一个可改变的自定义列表,要求记录列表中每个元素被访问的次数

class CountList:
   def __init__(self, *args):
      self.values = [x for x in args]
      self.count = {}.fromkeys(range(len(self.values)), 0)
   def __len__(self):
      return len(self.values)
   def __getitem__(self, item):
      self.count[item] += 1
      return self.values[item]
   def __setitem__(self, key, value):
      self.values[key] = value
   def __delitem__(self, key):
      del self.values[key]
      for i in range(0, len(self.values)):
           if i >= key:
              self.count[i] = self.count[i + 1]
      self.count.pop(len(self.values))
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
c2[2] = 12
print(c1[1] + c2[2]) # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}

迭代器

迭代是Python最强大的功能之一,是访问集合元素的一种方式;
迭代器是一个可以记住遍历的位置的对象;
迭代器对象从集合的第一个元素开始访问,直到所有元素访问完结束;
迭代器只会向前不会后退;
字符串、列表或元组对象都可用于创建迭代器
迭代器有两个基本的方法:iter()和next()
iter(object)用来生成迭代器
next(iterator[, default])返回迭代器的下一个项目
iterator:可迭代对象
default:可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发StopIteration异常

links = {'B': '百度', 'A': '阿⾥', 'T': '腾讯'}
it = iter(links)
print(next(it)) # B
print(next(it)) # A
print(next(it)) # T
print(next(it)) # StopIteration
it = iter(links)
while True:
   try:
     each = next(it)
   except StopIteration:
     break
   print(each)
# B
# A
# T
string = 'lsgogroup'
for c in string:
    print(c)
'''
l
s
g
o
g
r
o
u
p
'''
for c in iter(string):
   print(c)
links = {'B': '百度', 'A': '阿⾥', 'T': '腾讯'}
for each in links:
    print('%s -> %s' % (each, links[each]))
 
'''
B -> 百度
A -> 阿⾥
T -> 腾讯
'''
for each in iter(links):
    print('%s -> %s' % (each, links[each]))

把一个类作为一个迭代器使用需要在类中实现两个魔法方法__iter__()和__next__()
iter(self)定义当迭代容器中的行为,返回一个特殊的迭代器对象,这个迭代对象实现了__next__()方法并通过StopIteration异常标识迭代的完成
_next_()返回下一个迭代器对象
StopIteration异常用于标识迭代器的完成,防止出现无限循环的情况,在__next__()方法中我们可以设置在完成迭代次数后触发StopIteration异常来结束迭代

class Fibs:
   def __init__(self, n=10):
     self.a = 0
     self.b = 1
     self.n = n
   def __iter__(self):
     return self
   def __next__(self):
     self.a, self.b = self.b, self.a + self.b
     if self.a > self.n:
       raise StopIteration
     return self.a
fibs = Fibs(100)
for each in fibs:
    print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89

生成器

在Python中,使用了yield的函数称为生成器
生成器是一个返回迭代器的函数,只能用于迭代操作
在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行
调用一个生成器函数,返回的是一个迭代器对象

def myGen():
   print('⽣成器执⾏!')
   yield 1
   yield 2
myG = myGen()
print(next(myG)) 
# ⽣成器执⾏!
# 1
print(next(myG)) # 2
print(next(myG)) # StopIteration
myG = myGen()
for each in myG:
   print(each)
'''
⽣成器执⾏!
12
'''
def libs(n):
   a = 0
   b = 1
   while True:
      a, b = b, a + b
      if a > n:
         return
      yield a
for each in libs(100):
   print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89

练习题

类与对象

2.如何定义私有方法?
Python对于类的成员没有严格的访问控制限制,这与其他面相对对象语言有区别。关于私有属性和私有方法,有如下要点:
通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public);
类内部可以访问私有属性(方法);
类外部不能直接访问私有属性(方法);
类外部可以通过 ”_类名__私有属性(方法)名“ 访问私有属性(方法)
3.

class C:
   def myFun():
      print('Hello!')
   c = C()
   c.myFun()

错误原因:
myFun(self)
c=C() c.myFun()应和class C: 对齐

class Ticket:
    def __init__(self, total_price=0, price=100):
        self.price = price
        self.total_price = total_price

    # 统计票价函数
    def cost_ticket(self):
        day = int(input("您需要购买哪天的票?请输入1-7的正整数(1-5代表工作日,6-7代表周末):"))
        if day <= 0 or day > 7:
            errorDay = Exception("请检查您的购买日期!")
            raise errorDay
        adult = int(input("请输入需要购买大人的票数(张):"))
        if adult < 0:
            errorAdultTicket = Exception("请检查购买成人的票数!")
            raise errorAdultTicket
        child = int(input("请输入需要购买儿童的票数(张):"))
        if child < 0:
            errorChildTicket = Exception("请检查购买儿童的票数!")
            raise errorChildTicket
        if day in range(1, 6):
            self.total_price = adult * self.price + child * self.price * 0.5
        elif day in range(6, 8):
            self.total_price = adult * self.price * 1.2 + child * self.price * 1.2 * 0.5
        return self.total_price
try:
    price_total = CostTicket().cost_ticket()
    print("您需要支付{}元。".format(price_total))
except ValueError:
    print("请输入正确的整数!")
except Exception as errorResult:
    print(errorResult)

a=Ticket()
a.cost_ticket()

魔法方法

2.利用Python做一个简单的定时器类

import time as t
class MyTimer:
    def __init__(self):
        self.unit = ['年','月','天','小时','分钟','秒']
        self.prompt = "未开始计时"
        self.lasted = []
        self.begin = 0
        self.end = 0
    def start(self):
        self.begin = t.localtime()
        self.prompt = "提示:请先调用stop开始计时"
        print("计时开始......")
    def stop(self):
        if not self.begin:
            print("提示:请先调用start()开始计时")
        else:
            self.end = t.localtime()
            self._calc()
            print("计时结束")
    def _calc(self):
        self.lasted = []
        self.prompt = "总共运行了"
        for index in range(6):
            self.lasted.append(self.end[index] - self.begin[index])
            if self.lasted[index]:
                self.prompt += (str(self.lasted[index]) + self.unit[index])
        self.begin = 0
        self.end = 0
t1=MyTimer()
t1.prompt
t1.start()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值