Python的类和对象
类比介绍
要搞懂类和对象,可以以类比到实际生活经验帮助自身充分了解,例如建房子。
- 类(class):相当于施工图纸(blueprint)
- 对象(object):房子(已建成的)
注:
- 假设有施工图纸(blueprint),里面有房子的所有信息。
- 对着图纸开始憧憬这个新家后面的美好生活,家具细节,厨房美食来秀恩爱等……这些没有实际意义,只是想象,一切待房子建成后才行。
- 综上,只有图纸是没用的,必须在建好的房子里才可以。这些建好的房子就是对象,在Python中,一切皆为对象,有了这些对象才能做事情。
对于建立好的房子,每个人喜好不同,虽然房子和户型一样,但是根据施工图纸的不同,最后建造而来的对象随图纸不同而不同。
类与对象的关系
在Python中的类和对象介绍,以实际Python案例作为说明,如下:
变量赋值:a = 2
以往的解释:创建一个变量a,赋值2;相当于把2放进名叫a的篮子里……
请抛开上述的解释,用类和对象的角度去理解。
a = 2
回车后,Python中有一个较‘integer class’被唤醒,如下图:
ID | TYPE | VALUE |
---|---|---|
140720572109664 | <class ‘int’> | 2 |
integer class便是施工图纸,根据要求在执行区’创建’一个对象。那么对象便有integer class的对应特征或属性。
那么a = 2
中的a
便和上述的integer class挂钩了,即a是这个房子的钥匙,输入a时,这个房子便打开了。
隔壁新来了一个住户,也拥有了自己的新房子(对象),根据自己的图纸(类)创建了与a=2
不同的对象。
b = 3
则b对应的integer class为:
ID | TYPE | VALUE |
---|---|---|
140720572109696 | <class ‘int’> | 3 |
b
便与此挂钩,其作为标签绑在了新的对象上,此时就有两个分别绑在各自integer class的标签。
对于内容一致的对象
上述a = 2
和b = 3
属于不同类的不同对象。如果将b = 2
呢?
相当于邻居和你共住一个房子,即他没了另一个房子的钥匙,只有你这个房子的钥匙。
a = 2 b = 2
意味着,两者的integer class是同一个,即如下:
ID | TYPE | VALUE |
---|---|---|
140720572109664 | <class ‘int’> | 2 |
则b
也与这个integer class挂钩,自此a
和b
都作为标签绑在了同一个integer class上。
所以,不再是给变量a
赋值2,而是a
作为标签挂在数值2上。
下面将根据学术语句对对象和类进行说明,希望上述的方式能够助人理解类和对象。
对象 = 属性 + 方法
类主要定义对象的结构,以类为模板创建对象。类不仅包含方法定义,还包含所有实例共享的数据。
类有三个常用的特征:
1.封装:信息隐蔽技术。
可以使用关键字class
定义Python类,关键字后面紧跟类的名称、分号和类的实现。
【案例】创建海龟类
class Turtle:
'分为属性,方法两大板块'
#类的帮助信息,文档字符串
#属性
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')
- Python中的类名约定以大写字母开头。
- 类可以用单引号作为辅助信息。
- 属性即类的特征,方法即类可做的事情
tt = Turtle() #类的实例化
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98> 显现内存地址
print(type(tt))
# <class '__main__.Turtle'>
print(tt.__class__)
# <class '__main__.Turtle'>,即tt的type,显现实例的类型
print(tt.__class__.__name__)
# Turtle 显现实例对应类的名称
tt.climb()
# 我正在很努力的向前爬... 显现实例的方法
tt.run()
# 我正在飞快的向前跑...
tt.bite()
# 咬死你咬死你!!
# Python类也是对象。它们是type的实例
print(type(Turtle))
# <class 'type'>
2.类的继承
- 面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
- 通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
- 继承:子类自动共享父类之间数据和方法的机制。
【案例】
class Animals(object):
def __init__(self, name, age):
self.name = name
self.age= age
def eat(self):
print('eating......')
class Dog(Animals): # 当Dog没有构造方法时,执行Animals里面的构造方法
def __init__(self, name, age, power):
# self.name = name
# self.age = age
# 执行Dog的父类的构造方法;
super(Dog, self).__init__(name, age)
self.power = power
def eat(self):
print(self.power)
super(Dog, self).eat()
# 1. 如果子类没有的属性和方法, 则去父类找, 如果父类也没有, 就报错。
d1 = Dog("大黄",3,100)
print(d1.name)
print(d1.age)
print(d1.power)
d1.eat()
继承将在后文进行详解。
3.多态性
- 子类和父类存在相同方法时,子类会覆盖父类方法
- 运形时总会调用子类方法–> 多态
- 具体先了解"什么是self",再对多态性进行说明。
Self
Python 的 self
相当于 C++ 的 this
指针。(然而这句话并不懂 = =。)下面看些易懂的解释:
- self代表类的实例,而非类;即对象/实例 属性集合。
- 在Python类中规定函数的第一个参数是实例对象本身,并且约定俗成,把其名字写为self(改成‘this’也是可以的)。表示当前类的对象,可以调用当前类中的属性和方法。
- 由于类 可以起到模板的作用,故在创建实例的时候,可以将认为必须绑定属性强制填写,在 python中,是通过类中通常都会使用的一个方法,即def init(self) 方法(后文有介绍),在创建实例变量的时候,就把对应的属性绑上去。
- 若传入空参数则报错,必须传入与__init__方法匹配的参数,但是self不需要传,python解释器会自己把实例变量传进去。
- init 方法的第一个参数永远是 self ,表示创建的实例本身,因此,在 init 方法的内部,就可以把各种属性绑定到 self,因为 self 就指向创建的 实例本身。
【多个函数相互调用案例】
#self应用的多个函数相互调用案例
class Person(object):
def __init__(self,x,y):
self.x = x
self.y = y
def add(self):
sum = self.x + self.y
return sum
def square(self):
squr = pow(self.x,2)+pow(self.y,2)
return squr
def add_square(self):
c = self.add()+self.square() #类中的函数调用时候也要加self
return c
student = Person(3,4)
print(student.add())
print(student.square())
print('--------- 我是可爱的分割线,调用的函数-----------')
print(student.add_square())
加入默认形参,通过将其实例化在其他函数体内调用实例变量。
【加入默认形参案例】
class Person(object):
def __init__(self,x,y):
self.x = x
self.y = y
def add(self,z=16): # 设置 z 为实例变量,即 self.z = z, z 是 class 的一个成员了,而非普通局部变量
self.z = z
sum = self.x + self.y + z # z虽然已被实例化,但是依然可以当作 普通变量来用
return sum
def square(self):
squr = pow(self.x,2)+pow(self.y,2)
return squr
def add_square(self):
c = self.add()+self.square() + self.z # 调用实例变量 z
return c
student = Person(3,4)
print(student.add())
print(student.square())
print('--------- 我是可爱的分割线-----------')
print(student.add_square())
print(student.z) # 函数add 中的 z 被实例化以后,就可以利用实例化的方法访问它
- self看似是整个对象,实际描述了类是产生对象的过程,描述self就是得到了对象,所以self内的键值可以直接使用。
以上是对self整体的说明,除了self以外,下面介绍下常用于类中的__init__和公有&私有,进行全面的基础了解。
Python的魔法方法:init
- 类有一个名为
__init__(self[, param1, param2...])
的魔法方法,该方法在类实例化时会自动调用。 - 所有后面无需
return
【案例】
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 中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或变量就会为私有的了。
- 私有变量(private),只有内部可以访问,外部不能访问,私有变量是在名称前以两个下划线开头,如:__name,其实私有变量也不是完全不能被外部访问,不能直接访问是因为python解释器对外把 __name 变量改成了 _类名__name,所仍然**可以通过 _类名__name **来访问 __name .
- 变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
- 以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”
【案例说明】
类的私有属性实例
【案例】
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的私有为伪私有,通过“_类名__privatename__”调用是可行的
print(counter.__secretCount)
# AttributeError: 'JustCounter' object has no attribute '__secretCount'
类的私有方法实例
【案例】
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'
类的继承和多态详解
前面介绍了类的继承、多态、self、__init__和公私有的基本知识,下面举例详解类的继承和多态(重难点来了)。
Python 类的继承
(1) 调用父类方法
定义一个class
的时候,可以从某个现有的class继承,新的class称为子类,而被继承的class称为基类、父类或超类。
【案例详解】
先定义一个class Person
表示人,属性变量name
和sex
;
类可做的事情(方法)为print_title():
如果sex是male则显现man,否则woman。
【案例】
class Person():
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == 'male':
print("man")
else:
print("woman")
class Child(Person):
pass
May = Child('May','female') #子类Child继承父类Person的所有属性和方法
Peter = Person('Peter','male')
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title() #调用父类的属性
Peter.print_title()
- 编写Child类完全可以继承Person类(此处Child就是Person),使用
class Subclass_name(Baseclass_name)
表示继承。 - 子类获得了父类的全部属性和功能,Child类可以直接使用父类的print_title()方法。
- 实例化Child,子类继承了父类的构造函数,需要提供父类Person要求的两个属性变量
name
和sex
. - 继承关系中,如果一个实例的数据类型是某个子类,则可以被看作是父类。例如May既是Child又是Person。而反之不行,例如Peter仅是Person,而不是Child.
- 继承可以一级一级地继承下去,从爷爷到爸爸,再到儿子,而任何类最终可追溯到根类object。
判断继承的类型
Python在定义一个class的时候,实际上定义了一种数据类型,定义的数据类型和Python自带的数据类型,例如str、list、dict没什么两样。
Python有两个可用来判断继承的函数,isinstance()
可检查实例的类型,issubclass()
用来检查类继承。
isinstance(object,classinfo)
判断一个对象是否为一个已知类型
- isinstance认为子类是一种父类类型,考虑继承关系,与
type()
不同。 - 如果第一个参数不是对象,则永远返回
False
- 如果第二个参数不是类或者有类对象组成的额元祖,会抛出一个
TypeError异常
。
issubclass(class,classinfo)
判断参数class是否是类型参数classinfo的子类
- 一个类被认为是自身的子类。
classinfo
可以是类对象的元组,只要class
是其中任何一个候选类的子类,即返回True
,例如:
【案例】
print(isinstance(May,Child)) # True
print(isinstance(May,Person)) # True
print(isinstance(Peter,Child)) # False
print(isinstance(Peter,Person)) # True
print(issubclass(Child,Person)) # True
(2)类的多态(重写父类的属性)
在上述Child中重写print_title()
,若为male则print boy,否则为girl.
【案例】
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): # Child 继承 Person
def print_title(self): #定义函数
if self.sex == "male": #在子类中重置了方法
print("boy")
elif self.sex == "female":
print("girl")
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()
- 当子类和父类都存在相同的方法时,子类的print_title覆盖了父类的同个方法,运行代码时会调用子类的。
- 继承的另一个好处:多态,即需要传入更多的子类,例如新增Teenagers、Grownups等只需要继承Person类型即可,而print_title可以不重写,也可重写特有的。
- 引申开闭原则:
- 对扩展开放:允许子类重写方法函数
- 对修改封闭:不重写,直接继承父类方法函数
子类重写构造函数
子类可以没有构造函数,表示同父类构造一致;子类也可重写构造函数;现在需要在子类 Child 中新增两个属性变量:mother 和 father,我们可以构造如下(此处子类调用父类的构造方法):
【案例】
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): # Child 继承 Person
def __init__(self,name,sex,mother,father):
Person.__init__(self,name,sex) # 子类对父类的构造方法的调用,即上述的“self.name = name self.sex = sex”
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
- 此处调用父类的__init__不可直接调用,后面需要加上父类的调用才可,例如Person.init(self,name,sex)
- 如果父类的属性参数暂无,可以用super().init()代替
如果不对父类初始化直接赋值,而是在子类调用父类初始化过程中,增加需要的初始化参数。
【案例】
class Father():
def __init__(self,a,b):
self.a = a
self.b = b
def dev(self):
return self.a - self.b
class Son(Father):
def __init__(self,a,b,c=10):
Father.__init__(self,a,b)
self.c = c
def add(self):
return self.a + self.b
def compare(self):
if self.c > (self.a +self.b):
return True
else:
return False
son = Son(2,3)
print(son.dev())
print(son.add())
print(son.compare())
上述在调用子类Son时,只传入了a和b,如果显示地传入c会如何?
【案例说明】
son1 = Son(2,3,5)
print(son1.dev())
print(son1.add())
print(son1.compare())
可以看出即使是显性的将c赋值其他数字,在赋值后的答案会根据实际的赋值进行改变。
(3) 父类私有属性方法
对于前面讲的私有属性的调用,可以y应用supply()
函数和私有属性公用:
【案例】
class Father():
def __action(self): # 父类的私有方法
print('调用父类的方法')
class Son(Father):
def action(self):
super()._Father__action()
son=Son()
son.action()
多重继承
即爷爷下面不仅有爸爸还有孙女,例如新建一个Baby继承Child,可继承父类以及父类上层类的属性即方法,优先使用层类近的方法,参考如下:
【案例说明】
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
pass
class Baby(Child):
pass
May = Baby("May","female") # 继承上上层父类的属性
print(May.name,May.sex)
May.print_title() # 可使用上上层父类的方法
print("---------我是可爱的分割线喵----------")
class Child(Person):
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
class Baby(Child):
pass
May = Baby("May","female")
May.print_title() # 优先使用上层类的方法
组合类
【案例说明】
class Turtle:
def __init__(self, x):
self.x = x
class Fish:
def __init__(self, x):
self.x = 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.x, self.fish.x))
p = Pool(2, 3)
p.print_num()
类、类对象和实例对象基础
前面已经讲述了对象和类的区别以及案例应用,现在对两者的基础知识进行汇总。
- 类对象:创建一个类,其实也是一个对象也在内存开辟了一块空间,称为类对象,类对象只有一个。
class A(object):
pass
- 实例对象:就是通过实例化类创建的对象,称为实例对象,实例对象可以有多个。
【案例】
# 实例化对象 a、b、c都属于实例对象。
a = A()
b = A()
c = A()
- 类属性:类里面方法外面定义的变量称为类属性。类属性所属于类对象并且多个实例对象之间共享同一个类属性,即类属性所有的通过该类实例化的对象都能共享。
【案例】
class A():
a = 0 # 类属性
def __init__(self, x):
# 使用类属性可以通过 (类名.类属性)调用。
A.a = xx
类属性和实例属性区别
- 类属性:类外面,可通过
实例对象、类属性
和类名、类属性
进行调用。类里面,通过self.类属性
和类名.类属性
进行调用。(具体下面案例有介绍) - 实例属性:类外面,可以通过
实例对象,实例属性
调用,类里面通过self.实例属性
调用。 - 实例属性就相当于局部变量。出了对应的类和这个类的实例对象就没有作用了。
- 类属性就相当于类里面的全局变量,可以和这个类的所有实例对象共享。
【案例】
# 创建类对象
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 所谓的绑定概念。
Python对象的数据属性通常存储在名为.__dict__
的字典中,可以直接方位__dict__
,或利用Python的内置函数vars()
获取.__dict__
.
类的__dict__属性和类对象__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) #实例化赋值后的属性,即''x:x,'y':y'
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}
相关的内置函数(BIF)
除了上述的isinstance
和issubclass
函数以外,另有以下函数常用:
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, 'no')) # False
getattr(object,name[,default])
用于返回一个对象属性值,如果没有对应的name且有default参数,就返回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
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
delattr(object,name)
用于删除属性。
【案例】
class Coordinate:
x = 10
y = 3
z = 0
p = Coordinate()
print('x=',p.x)
print('z=',p.z)
delattr(Coordinage,'z')
print('---删除z属性后--')
print('x=',p.x)
print('z=',p.z)
class property([fget[,fset[,fdel[,doc]]]])
用于新式类中返回属性值
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
del cc.x
print(cc.x) # AttributeError: 'C' object has no attribute '_C__x'
练习题
- 以下类定义中哪些是类属性,哪些是实例属性?
class C:
num = 0
def __init__(self):
self.x = 4
self.y = 5
C.count = 6
答:类属性有num = 0,C.count=6;实例属性有self.x=4,self.y=5.
-
怎么定义私有方法?
答:私有化:“__”其函数或变量就是私有的,例如def__name(self).
私有变量:实例.__类名__变量名
私有方法:实例.__类名__方法名() -
尝试执行以下代码,并解释错误原因:
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
答:c = C()是将类实例化,应当注意缩进,最好有一定空格距离,如:
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
- 按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
要求:
- 平日票价100元
- 周末票价为平日的120%
- 儿童票半价
class Ticket():
def __init__(self,day,num1,num2):
self.day = day
self.num1 = num1
self.num2 = num2
l1 = ['Mon','Tues','Wed','Thu','Frid']
l2 = ['Sat','Sun']
if day in l1:
print('This is weekday')
else:
print('This is weekend')
def weekday(self,num1,num2):
self.num1 = num1
self.num2 = num2
money1 = num1 * 100
money2 = num2 * 100/2
money = money1 + money2
print("All of these tickets:%d"%money)
def weekend(self,num1,num2):
self.num1 = num1
self.num2 = num2
money1 = num1 * 120
money2 = num2 * 120/2
money = money1 + money2
print("All of these tickets:%d"%money)
a = Ticket('Mon',2,1)
a.weekday(2,1)
参考文献:
https://blog.csdn.net/zcx1203/article/details/89187495
https://www.cnblogs.com/magicking/p/8971740.html
https://www.cnblogs.com/jessonluo/p/4717140.html
https://blog.csdn.net/xrinosvip/article/details/89647884
https://www.cnblogs.com/feeland/p/4419121.html
https://blog.csdn.net/yilulvxing/article/details/85374142
https://www.runoob.com/python/python-func-super.html
https://www.cnblogs.com/alvin2010/p/9102344.html
Python魔法方法
- 魔法方法总是被下划线包围,例如
__init__
,是面向对象的Python的一切。 - “魔力”在于它们总能在适当的时候被自动调用,也可以定义自己想要的行为,均自动发生。
- 两个参数:第一个参数为
cls
(类方法)或者self
(实例方法)。 cls
:代表一个类的名称。self
:代表一个实例对象的名称。- 认识它可以真正开始意识到面向对象的Python的强大,然后随之而来的是责任,了解其正确的方法非常重要。
基本魔法方法:
__init__(self[,...])
构造器,当一个实例被创建的时候调用的初始方法。
- 新建的实力本身连带其中的参数,会一并传给
__init__
函数并自动执行它,所以函数的参数列表在开头多出一项(命名为self
),永远指代新建的那个实例对象。 __init__
不允许有返回值,如果其过于复杂需要提前结束,可以使用单独的return
就好,不带返回值。
#案例说明
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
__new__(cls[,...])
在一个对象实例化的时候所调用的第一个方法,调用__init__
初始化前,先调用__new__
.
- 其第一个参数
cls
代表实例化的类,此参数在实例化时由Python解释器自动提供,其他的参数是用来直接传递给__init__
方法。 __new__
决定是否使用__init__
方法,因为__new__
可以调用其他类的构造方法或直接返回别的实例化对象作为本类的实例化。若__new__
没有返回实例对象,则__init__
不会被调用。__new__
主要是用于继承一个不可变得类型,不如一个tuple
或string
。
#案例说明1
class A(object):
def __init__(self,value):
print("info A __init__")
self.value = value
def __new__(cls,*args, **kwargs):
print("info A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self,value):
print("info B __init__")
self.value = value
def __new__(cls,*args,**kwargs):
print("info B __new__")
print(cls)
return super().__new__(cls,*args,**kwargs)
b = B(5)
#案例说明2
class A(object):
def __init__(self,value):
print("info A __init__")
self.value = value
def __new__(cls,*args, **kwargs):
print("info A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self,value):
print("info B __init__")
self.value = value
def __new__(cls,*args,**kwargs):
print("info B __new__")
print(cls)
return super().__new__(A,*args,**kwargs)
#返回的实例对象为A
b = B(5)
若__nuew__
没有正确返回当前类cls
的实例,那__init__
是不会被调用的,即使是父类的实例也不行,将没有__init__
被调用。
#`__nuew__`实现单例模式
class Earth:
pass
a = Earth()
print(id(a))
b= Earth()
print(id(b))
#一个类只有一个实例:定义一个类属性做判断
class Earth:
__instance = None # 定义一个类属性做判断
def __new__(cls):
if cls.__instance is None: #如果instance为空则说明是第一次创建实例
cls.__instance = object.__new__(cls)
#调用父类的__new__(cls)创建实例,并赋予类变量instance,此时的该变量从None变为实例
return cls.__instance #返回实例对象
else:
return cls.__instance #返回上一个对象的引用
a = Earth()
print(id(a))
b = Earth()
print(id(b))
__new__
方法主要是当继承一些不可变得class时,例如int,str,tuple
,提供一个自定义这些类的实例化过程的途径。
class CapStr(str):
def __new__(cls,string):
string = string.upper()
return str.__new__(cls,string)
a = CapStr("i love python.")
print(a)
__del__(self)
析构器,当一个对象将要被系统回收之时调用的方法。
- 创建对象后,python解释器默认调用
__init__
方法 - 删除对象时,python解释器也会默认调用一个方法,即
__del__()
Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。
大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。
# 案例说明
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
__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('Tom',8)
print(Tom) #调用__str__
print(str(Tom))
print(repr(Tom))
Tom.eat()
Tom.introduce()
应用区别:(例如datetime)
-
_str__(self)
的返回结果可读性强。也就是说,__str__
的意义是得到便于人们阅读的信息。 -
__repr__(self)
的返回结果应更准确。怎么说,__repr__
存在的目的在于调试,便于开发者使用。
#datetime案例说明
import datetime
today = datetime.date.today()
print(str(today))
print(repr(today))
print('%s'%today) #str()处理对象
print('%r'%today)#repr()处理对象
其他基本方法:
运算符 | 定义 |
---|---|
__call__(self,[,args...]) | 允许一个类的实例像函数一样被调用:x(a,b) 调用x.__call__(a,b) |
__len__(self,other) | 定义被len() 调用时的行为 |
__bytes__(self,other) | 定义被bytes() 调用时的行为 |
__hash__(self,other) | 定义被hash() 调用时的行为 |
__bool__(self,other) | 定义被bool() 调用时的行为 |
__format__(self,other) | 定义被format() 调用时行为 |
#案例说明
class A(object):
def __call__(self,x):
print('__call__called,print x:',x)
a = A()
a('123') #a实际就是类对象A的实例对象,实例对象能像函数一样传参并被调用就是__call__()方法的功能
2.有关属性:
运算符 | 定义 |
---|---|
__getattr__(object,mame[,default]) | 定义当用户试图获取一个不存在的属性时,赋予default |
__getattribute__(self,other) | 定义当该类的属性被访问时的行为(先调用,查看是否存在该属性,若不存在,则去调用__getattr__ ) |
__setattr__(self,other) | 定义当一个属性被设置时时的行为 |
__delattr__(self,other) | 定义当一个属性被删除时的行为 |
__dir__(self,other) | 定义当dir() 被调用时的行为 |
__get__(self,instance,owner) | 定义当描述符的值被取得时的行为 |
__set__(self,instance,value) | 定义当描述符的值被改变时的行为 |
__delete__(self,instance) | 定义当描述符的值被删除时的行为 |
#案例说明(属性访问)
class A():
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)
a = A()
a.x
print("------我是可爱的下划线----")
a.x = 1
del a.x
#案例说明(描述符)
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
print("-------------------")
t.x = 'x-man'
del t.x
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) | 定义当被power()调用或**运算的行为 |
__lshift__(self,other) | 定义按位左移的行为 << |
__rshift__(self,other) | 定义按位右移的行为 >> |
__add__(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()的代码
main()
补充说明 if __name__ = '__main__'
- 如果是直接执行某个.py文件的时候,该文件中那么”name == ‘main’“是True,但如果从另外一个.py文件通过import导入该文件的时候,并且调用这个文件中的函数时,这是函数里面__name__的值就是我们这个py文件的名字而不是__main__。
- 这个功能还有一个用处:调试代码的时候,在”if name == ‘main’“中加入一些的调试代码,可以让外部模块调用的时候不执行调试代码,但是如果想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!
4.反算术运算符
- 反运算魔法方法与算术运算符保持一一对应,不同之处就是反运算的魔法方法多了一个"r",当文件左操作不支持相应的操作时被调用。
运算符 | 定义 |
---|---|
__radd__(self,other) | 定义加法的行为: + ,当左操作数不支持相应的操作时被调用 |
__rsub__(self,other) | 定义减法的行为: - ,当左操作数不支持相应的操作时被调用 |
__rmul__(self,other) | 定义乘法的行为: * ,当左操作数不支持相应的操作时被调用 |
__rtruediv__(self,other) | 定义真除法的行为: / ,当左操作数不支持相应的操作时被调用 |
__rfloordiv__(self,other) | 定义整数除法(地板除)的行为: // ,当左操作数不支持相应的操作时被调用 |
__rmod__(self,other) | 定义取模算法的行为: % ,当左操作数不支持相应的操作时被调用 |
__rdivmod__(self,other) | 定义当被divmod() 调用时的行为: +divmod(a,b) 把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a//b, a%b) ,当左操作数不支持相应的操作时被调用 |
__rpow__(self,other) | 定义当被power()调用或**运算的行为 ,当左操作数不支持相应的操作时被调用 |
__rlshift__(self,other) | 定义按位左移的行为 << ,当左操作数不支持相应的操作时被调用 |
__rrshift__(self,other) | 定义按位右移的行为 >> ,当左操作数不支持相应的操作时被调用 |
__rxor__(self,other) | 定义按位异或的行为: ^ ,当左操作数不支持相应的操作时被调用 |
__ror__(self,other) | 当左操作数不支持相应的操作时被调用,定义按位或操作的行为: | |
举例说明:a + b
:
加数是a,被加数是b,因此a是主动,反运算就是若a对象的__add__()
方法没有实现或者不支持相应的操作,则Python就会调用b的__add__()
方法。
#案例说明
class Nint(int):
def __radd__(self,other):
return int.__sub__(other,self) #注意:self与other互换了位置
a = Nint(5)
b = Nint(3)
print(a + b) #因为a对象有默认的add(),所以b的__radd__没有执行
print(1 + b) #因1本身没有任何方法,所以直接调用b的__radd__()
print(2 + a) #同上
5.增量赋值运算符
运算符 | 定义 |
---|---|
__iadd__(self,other) | 定义加法的行为: += |
__isub__(self,other) | 定义减法的行为: -= |
__imul__(self,other) | 定义乘法的行为: *= |
__itruediv__(self,other) | 定义真除法的行为: /= |
__ifloordiv__(self,other) | 定义整数除法(地板除)的行为: //= |
__imod__(self,other) | 定义取模算法的行为: %= |
__ipow__(self,other) | 定义当被power()调用或**= 运算的行为 |
__ilshift__(self,other) | 定义按位左移的行为 <<= |
__irshift__(self,other) | 定义按位右移的行为 >>= |
__iand__(self,other) | 定义按位与操作的行为: &= |
__ixor__(self,other) | 定义按位异或的行为: ^= |
__ior__(self,other) | 定义按位或操作的行为: /|= |
6.一元运算符
__neg__(self)
定义正号的行为:+x
__pos__(self)
定义负号的行为:-x
__abs__(self)
定义当被abs()
调用时的行为__invert__(self)
定义按位求反的行为:~x
7.定制序列(容器类型)
协议与其他编程语言中的接口很相似,规定哪些方法必须定义。然而在Python中的协议就显得不那么正式。Python中协议更像是一种指南。
运算符 | 定义 |
---|---|
__len__(self) | 定义len() 调用时的行为 |
__getitem__(self,key) | 定义获取容器中指定元素的行为,相当于 self[key] |
__setitem__(self,other) | 定义设置容器中指定元素的行为,相当于 self[key] = value |
__delitem__(self,other) | 定义删除容器中指定元素的行为,相当于 del self[key] |
__iter__(self,other) | 定义当迭代容器中的元素的行为 |
__reversed__(self,other) | 定义当被 reversed() 调用时的行为 |
__contains__(self,other) | 定义当使用成员测试运算符(in 或 not in)时的行为 |
- 如果说你希望定制的容器是不可变的话,你只需要定义
__len__()
和__getitem__()
方法。 - 如果你希望定制的容器是可变的话,除了
__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])
print(c2[2])
print("-------------------")
print(c1[1]+c2[2])
print(c1.count)
print(c2.count)
print(c1.count)
#案例:编写一个可改变的自定义列表,要求记录列表中每个元素被访问的次数
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,4,5,9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])
print(c2[2])
print("-------------------")
c2[2] = 30 #设置元素
print(c1[1]+c2[2])
print(c1.count)
print(c2.count)
del c1[1]
print(c1.count)
8.生成器
- 在Python中,这种一边循环一边计算的机制,称为生成器:generator.
- 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成()
L = [x * x for x in range(10)]
G = (x * x for x in range(10))
print("list:",L)
G
generator
保存的是算法,使用for循环来迭代它,并且不需要next()
不用关心StopIteration
的错误。- 如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用
yield()
函数来实现。 - 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行
next()
方法时从当前位置继续运行。 - 调用一个生成器函数,返回的是一个迭代器对象。
#斐波那契数列
def libs(n):
a = 0
b = 1
while True:
a,b = b, a+b
if a > n:
return
else:
yield a
for value in libs(100):
print(value,end = ' ')
9.迭代器
- 迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
- 迭代器只能往前不会后退。
- 字符串,列表或元组对象都可用于创建迭代器:
#案例说明
string = 'lsgogroup'
for c in iter(string):
print(c)
for c in string:
print(c)
都是一下答案:
-
可以直接作用于for循环的数据类型统称为可迭代对象
Iterable
,有以下几种: -
一类是集合数据类型,如list、tuple、dict、set、str等;
-
一类是
generator
,包括生成器和带yield
的generator function
。 -
而生成器不但可以作用于for循环,还可以被
next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。可以被next()
函数调用并不断返回下一个值的对象称为迭代器Iterator
. -
生成器都是
Iterator
对象,但list、dict、str虽然是Iterable
,却不是Iterator
。把list、dict、str等Iterable
变成Iterator
可以使用iter()
函数。 -
Python的for循环本质上就是通过不断调用
next()
函数实现。
#斐波那契数列案例说明
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=' ')
#杨辉三角数列案例
#每一行做一个list,先写一个generator,不断输出下一行的list
def triangle():
L = [1]
while True:
yield L
L = [1] + [L[i] + L[i+1] for i in range(len(L)-1)] + [1]
#再利用for循环遍历每一个List,append到新的列表中
n = 0
result = []
t = triangle()
for i in t:
print(i)
result.append(t)
n += 1
if n == 10:
break
参考文献:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640
https://www.zhihu.com/question/46973549/answer/767530541
https://zhuanlan.zhihu.com/p/141212114
https://www.cnblogs.com/qunxiadexiaoxiangjiao/p/8330718.html
https://www.cnblogs.com/gcgc/p/11585444.html
https://www.cnblogs.com/hmw112626/p/9812675.html
https://www.runoob.com/python/python-func-getattr.html
https://www.cnblogs.com/fierydragon/p/12910281.html
https://www.cnblogs.com/pachongshangdexuebi/p/4729341.html