目录
0x00 定义一个类(__init__,__str__)
0x01 类的私有属性:
0x02 私有方法:
0x03 __del__方法:
0x04 引用计数:
0x05 继承:
0x06 重写:
0x07 多继承:
0x08 多继承的注意点:
0x09 多态:
0x0A 类属性、实例属性:
0x0B 类方法,实例方法,静态方法:
0x0C 类与实例对象的魔术字典属性
0x0B __getattr__()魔术方法:
0x0C 什么是高耦合?什么是低耦合?
0x0D __new__方法:
0x0E创建单例对象:
异常处理:
如何抛出自定义异常:
异常处理中抛出异常:
if的各种真假判断:
"""
class 类名(大驼峰命名):
#类属性
#实例方法
def xxxx(self): #当类实例化为对象的时候,对象所占内存的首地址自定赋值给self
pass
"""
class Cat:
#构造函数,创建对象时会自动调用,一般被用来初始化对象
#def __init__(self):
#print("无参构造器")
def __init__(self,new_name,new_age):
print("有参构造器")
self.name = new_name
self.age = new_age
def __str__(self):
#当调用print(对象名)的时候就会自动调用该魔术方法,返回描述信息。
#对象名实际上存储了对象的首地址
xxxxx
xxxxx
xxxxx
return "描述信息:%s的年龄是:%d"%(self.name,self.age)
def eat(self): #不一定叫self,但是必须得有一个变量作为第一个参数,来自动存储所占内存的首地址
print("猫正在吃鱼。。")
def introduce(self):
print("%s的年龄是%d"%(self.name,self.age))
#创建一个对象
#Tom = Cat()#开辟一段内存空间创建对象,自动调用__init__方法,返回内存空间的地址
Tom = Cat("tom","64")#自动调用有参构造器
#调用对象的方法
Tom.eat()
#给对象添加属性
#Tom.name = "tom"
#Tom.age =60
#Tom.introduce()
lanMao = Cat()
lanMao.name="lanmao"
lanMao.age = 10
lanMao.introduce()
print(lanMao)
隐藏对象的属性:设置属性和获取属性的时候 用方法去间接设置。这样在方法内部就能加入判断。
class Dog:
def set_age(self,new_age):
if new_age>0 and new_age <=100:
self.age = new_age
else:
self.age = 0
def get_age(self):
return self.age
dog = Dog()
dog.set_age(10)
dog.get_age()
私有属性必须在类的内部被添加,并且以两个下划线开头来表明是私有属性。在类的外部通过对象来添加的属性,即使以两个下划线开头也不是私有属性。私有属性只能在类内部被读写,在类外部不能通过对象直接访问,除非用对象名._类名__私有属性名 来访问。
class Dog:
__color = "black" #私有属性
def __init__(self,new_name,age):
self.name = new_name
self.__age = age #私有属性,能被成员函数访问
dog = Dog("dahei",12)
print(dog.__color) #error
print(dog._Dog_color) #right
dog.__size = "big" #不是私有属性
print(dog.__size) #right
在方法名前添加两个下划线表明一个方法为私有方法,私有方法只能被成员函数调用。不能被对象点访问。
class Dog:
def __test1(self):#私有方法
print("----1-----")
def test2(self): #公有方法
print("----2-----")
dog = Dog()
dog.__test1()#error
dog.test2()
方法生来就是被对象调用的,不能被对象调用的私有方法有什么用呢?其实,私有方法只是不能被对象直接调用,可以通过调用公有方法来间接调用私有方法,通过在公有方法中加入判断语句和私有方法,我们可以实现 有条件的间接调用一个方法 的效果。这就是私有方法存在的意义。
class Phone:
#私有方法
def __send_msg(self):
print("----正在发送短信-----")
#公有方法:
def send_msg(self,new_money):
if new_money > 10000:
self.__send_msg()
else:
print("余额不足,请充值后再发送短信")
iphone = Phone()
iphone.send_msg(1000000)
析构函数,当对象占据的内存空间被释放的时候,会自动调用这个魔术方法
class Dog:
def __del__(self):
print("对象占据的内存空间被释放了")
dog1 = Dog()
dog2 = dog1
del dog1 #只是删除了dog1中存储中的对象的地址
print("="*20)
del dog2 #删除了dog2中存储的对象的地址,但是由于dog2是最后一个存储对象地址的变量,所以对象占据的内存空间也被释放了。
print("+"*20)
import sys
class T:
pass
t = T()
sys.getrefcount(t) #返回t所指向的对象的引用计数 =2 因为在调用这个方法时将实参的值还传递给了形参所以不是1而是2
class Animal:
def eat(self):
print("eat")
def drink(self):
print("drink")
class Dog(Animal): #表示Dog继承自Animal
def bark(self):
print("汪汪叫")
class Xiaotq(Dog):
def fly(self):
print("飞")
a = Animal()
b = Dog()
b.eat()
class Animal:
def eat(self):
print("eat")
def drink(self):
print("drink")
class Dog(Animal): #表示Dog继承自Animal
def bark(self):
print("汪汪叫")
class Xiaotq(Dog):
def fly(self):
print("飞")
a = Animal()
b = Dog()
b.eat()
那么假如父类中的方法被重写之后,我们还想再子类中调用该方法该怎么办呢?
class Animal:
def eat(self):
print("eat")
def drink(self):
print("drink")
class Dog(Animal): #表示Dog继承自Animal
def bark(self):
print("汪汪叫")
class Xiaotq(Dog):
def fly(self):
print("飞")
def bark(self):
print("---狂叫----")
print("敌人太强大了,我怂了")
#方法一:
Dog.bark(self) #调用被重写的方法
#方法二:
super().bark()
a = Animal()
b = Dog()
b.eat()
私有方法和私有属性不能被继承。并且在派生类中也不能访问私有属性和私有方法!但是如果将它们封装在一个公有方法中,我们通过继承这个公有方法,来调用私有属性和私有方法则是可以实现的。
class A:
def __init__(self):
self.num1 = 100
self.__num2 = 100
def test1(self):
print("----test1----")
def __test2(self):
print("----test2----")
def test3(self):
self.__test2()
print(self.__num2)
class B(A):
def test4(self):
self.__test2() #错误,派生类中无法访问私有属性和私有方法
print(self.__num2)
b = B()
b.test1()
#b.__test2() #私有方法并不会被继承
print(b.num1)
#print(b.__num2) #私有属性 并不会被继承
b.test3()
b.test4() #错误!
class Base(object):
def test(self):
print("___base___")
class A(Base):
def test1(self):
print("---test1")
class B(Base):
def test2(self):
print("---test2")
class C(A,B): #表示C既继承A也继承B
def test3(self):
print("---test")
c = C()
c.test1()
1.子类中的同名方法会覆盖父类中的同名方法
class Base(object):
def test(self):
print("___base___")
class A(Base):
def test(self):
print("---test1")
class B(Base):
def test(self):
print("---test2")
class C(A,B): #表示C既继承A也继承B
def test(self):
print("---test")
c = C()
c.test() #子类中的同名方法将会覆盖父类中的方法
#那么假如子类C中没有test方法,这里到底会调用A中的test方法呢?还是会调用B中的test方法呢?这时python就会按照继承链的顺序逐个往上寻找,先在哪个类中找到test()方法,先调用谁的test方法。通过
print(子类名.__mro__) 可以输出子类的继承链
print(C.__mro__)
2.新式类和旧式类继承链的不同
class A://旧式类
def __init__(self):
a = 10
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
obj_d = D()
print(obj_d.a)
//当D中没有a这个属性时,旧式类会采用深度搜索的方式去寻找a。即先找D中有没有,再找B中有没有,然后找A中有没有,然后找C中有没有,然后再找A中有没有
//如果A是一个新式类,则会采用广度搜索的方式的去寻找a。即先找D中有么有,然后找B 和C中有没有。然后找A中有没有
class Dog(object):
def print_self(self):
print("大家好,我是xxx,希望以后大家多多关照")
class Xiaotq(Dog):
def print_self(self):
print("Hello everybody,我是你们的老大,我是xxx")
def introduce(temp): #多态
temp.print_self()
dog1 =Dog()
dog2 = Xiaotq()
introduce(dog1)
introduce(dog2)
class Tool(object):#专门开辟一段内存空间,存储定义类的代码。类名存储了这段内存空间的首地址。其实python中类也是一种的对象,称为类对象,对象称之为实例对象,实例对象中的属性称之为实例属性,类对象中的属性称之为类属性
#属性:
num = 0#类属性
#方法:
def __init__(self,new_name):
self.name = new_name
#self.num +=1 #错误!类属性并没有存储在实例对象中,不能通过实例对象的首地址来获取到类属性的地址来修改它的值。
Tool.num +=1 #正确,类属性存储在类中,通过类的首地址可以找到并修改它
tool1 = Tool("铁锹") #开辟一段内存空间,只存储属性,并不存储定义方法的代码。其中有一个特殊的属性,存储了定义对象的类的代码的地址。也就说当需要调用方法时,直接根据定义类的代码来调用。
tool2 = Tool("工兵铲")
tool3 = Tool("水桶")
print(Tool.num)
实例属性:和具体的某个实例对象有关系,并且一个实例对象 和另外一个实例对象时不共享属性的。
类属性:多个对象之间共享同一个类属性,当一个对象修改了类属性后,其他对象看到的类属性也就修改了。
class Game(object):
#类属性
num = 0
#实例方法:
def __init__(self):
#实例属性:
self.name = "laowang"
def test(self):
print("test")
#类方法:只要在方法前加上@classmethod 那么这个方法就是类方法.类方法的括号内一般写cls,用来保存 类的首地址
@classmethod
def add_num(cls):
cls.num = 100
#静态方法:类方法和实例方法必须得有一个参数,但是静态方法可以没有任何参数
@staticmethod
def print_menu():
print("xxxx")
game = Game()
Game.add_num() #类方法存储在为类开辟的地址中,所以可以通过类的首地址来调用类方法
game.add_num() #类实例化后会在对象中保存类的引用,所以也可以通过对象来调用类方法
print(Game.num)
#通过类和对象都可以调用静态方法。
Game.print_menu()
game.print_menu()
#调用实例方法
game.test()
Game.test(game)#通过类名调用实例方法必须将对象的引用作为参数传入方法
想要对类属性进行操作的时候,往往用类方法,当想要对实例属性进行操作的时候往往用实例方法,当我们想进行一个简单的操作既和类属性没有关系也和实例属性没有关系的时候,可以用静态方法。
dir(对象名) 返回该对象的所有属性。
dir(类名) 返回类的所有属性
通过调用这两个函数可以看到,实例对象和类都有一个魔术属性——___dict___
实例对象的魔术字典属性中会以键值对的方式存储该对象的所有实例属性
类的魔术字典属性中存储了该类的所有类属性、所有类方法、所有实例方法
当我们执行 对象名.属性名 这句话访问对象某个属性时,底层实际上会遍历对象的魔术字典,看其中有没有存储这个属性和其值的键值对。
class Person(object):
#类属性:
grade = 1
clas = 10
def __init__(self,name,age,height,weight):
#实例属性:
self.name = name
self.age = age
self.height = height
self.weight = weight
def change_name(self,name):
self.name = name
@classmethod
def change_grade(cls,grade):
cls.grade = grade
@classmethod
def change_clas(cls,clas):
cls.clas = clas
xiaoming = Person("xiaoming",18,175,60)
print("xiaoming对象的所有属性:")
print(dir(xiaoming))
print("xiaoming.__dict__的值:")
print(xiaoming.__dict__)
print("Person类的所有属性:")
print(dir(Person))
print("Person.__dict__的值:")
print(Person.__dict__)
那么当我们通过 对象名.类属性 访问类属性时底层是如何操作的呢?
首先底层会遍历对象的魔术字典看有没有叫这个名字的属性,然后会遍历类的魔术字典,看有没有叫这个名字的属性。
如果在两者中都找不到这个属性,那么底层会自动调用对象的__getattr__()方法。
#coding:utf-8
class Foo(object):
def __init__(self):
pass
def __getattr__(self,item):
#item为属性名,当通过对象.属性 访问对象的某一个属性时,
#假如对象没有这个属性,那么就会自动调用__getattr__方法,
#并且将该属性自动赋值给item。
print(item+" ",end="")
if(item != "itcast"):
return self
else:
return " "
print(Foo().think.different.itcast)
分析:首先Foo()返回值为一个实例对象(的地址)。然后先执行实例对象.think,发现没有这个属性,所以默认调用__getattr__方法,并将think自动传给item。然后这个函数返回self,也就是实例对象的地址。那么接下来就会执行实例对象.different。过程同上。
执行结果:
由上可知,当我们执行 对象名.属性名 这句话访问对象某个属性时,底层实际上会先遍历对象的魔术字典__dict__,看其中有没有存储这个属性和其值的键值对。如果没有,则自动调用__getattr__方法,将该方法的返回值返回。那么是谁规定底层这样做的呢?
就是__getattribute__()方法!
这个方法一般很少去重写,因为一不小心,就可能陷入无穷无尽的死循环之中。例如:
def __getattribute__():
self.age = 10;
self.age会自动调用__getattribute__方法,这样就陷入了死循环之中。
通俗地讲,假如组成程序的不同模块之间的关联程度比较强,修改了一个模块,另一个模块也不得不修改,那么就叫高耦合。而所谓低耦合就是指程序的不同模块之间尽可能相互独立,不相互影响。将耦合性降低的过程称之为“解耦”
object类中写好了__new__方法,它的作用是自动为对象开辟一段内存空间,来存储对象。那么假如我们在类中重写了object类中的__new__方法,会怎么样呢?
class Dog(object):
def __init__(self):
print("__init__")
def __del__(self):
print("__del__")
def __str__(self):
print("__str__")
def __new__(cls):
#注意自动传给new方法的是类的首地址,而不是对象的首地址,
#因为new方法只有知道了类的首地址,才能开辟内存,创建对象,
#然后返回对象的首地址。
print("__new__")
dog = Dog()
#只输出了__new__,却没有输出__init__和__del__。
#这说明当__new__方法被我们如此重写后,失去了创建一个新的对象的功能。
#那么如何可以让重写的__new__方法也具有创建新对象的功能呢?
#这就需要求助于原来的__new__方法了。
修改后:
class Dog(object):
def __init__(self):
print("__init__")
def __del__(self):
print("__del__")
def __str__(self):
print("__str__")
def __new__(cls):
print("__new__")
return object.__new__(cls)
#调用原来的new方法,原来new方法必须知道类的首地址,
#才能创建对象,返回对象的首地址。
dog = Dog()
#这一句话相当于要做3件事情:
“”“
1.调用__new__方法 来创建对象(前提是new方法必须有创建对象的功能)
2.创建对象成功后会立即自动调用 构造函数 __init__方法,先并将对象的首地址赋值给__init()__方法的self变量
3.然后将对象的首地址返回给 变量 dog
[注意只要调用了__new__方法 就一定会调用 __init__方法]
”“”
(单例模式)即不论创建多少次,都是同一个对象。
class Dog(object):
__flag = 0
__index = None#私有的类属性防止外界访问
def __new__(cls):
if flag == 0 :
cls.index = object.__new__(cls)
cls.flag = 1
return cls.index
else:
return cls.index
a = Dog()
print(id(a))
b = Dog()
print(id(b)) #不论创建多少次,都是同一个对象
其实在一个类中创建两个变量有点多余,创建一个变量就足够了
class Dog(object):
__flag = 0
私有的类属性防止外界访问
def __new__(cls):
if flag == 0 :
cls.flag = object.__new__(cls)
return cls.flag
else:
return cls.flag
a = Dog()
print(id(a))
b = Dog()
print(id(b)) #不论创建多少次,都是同一个对象
实现只初始化一次对象:
class Dog(object):
__flag = 0
__index = None#私有的类属性防止外界访问
def __new__(cls,name):
if flag == 0 :
cls.index = object.__new__(cls)
cls.flag = 1
return cls.index
else:
return cls.index
def __init__(self,name):
self.name = name
a = Dog("旺财")#注意这里的传参既会传给__init__()方法,也会传给__new__()方法。
"""
调用__new__方法 >> 调用 ___init__()方法 >> 返回对象首地址
"""
print(id(a))
print(a.name) #旺财
b = Dog("狗子")
“”“
调用__new__方法 >> 调用 ___init__()方法 >> 返回对象首地址
虽然两者是同一个对象,但是只要调用了__new__方法就一定会调用__init()方法
”“”
print(id(b))
print(b.name) #狗子
那么如何做到只初始化一次对象呢?
class Dog(object):
__flag = 0
__index = None#私有的类属性防止外界访问
__init_flag = False
def __new__(cls,name):
if flag == 0 :
cls.index = object.__new__(cls)
cls.flag = 1
return cls.index
else:
return cls.index
def __init__(self,name):
if Dog.__init_flag == True:
pass
else:
self.name = name
Dog.__init_flag == True
a = Dog("旺财")#注意这里的传参既会传给__init__()方法,也会传给__new__()方法。
"""
调用__new__方法 >> 调用 ___init__()方法 >> 返回对象首地址
"""
print(id(a))
print(a.name) #旺财
b = Dog("狗子")
“”“
调用__new__方法 >> 调用 ___init__()方法 >> 返回对象首地址
虽然两者是同一个对象,但是只要调用了__new__方法就一定会调用__init()方法
”“”
print(id(b))
print(b.name) #狗子
制定预处理方案,而不是让程序直接崩了。
try:
print(num) #如果出现异常,就直接将异常抛给except语句块。异常之后的代码就不执行了。开始从except语句块执行代码。
print("----11-----")
except 异常的名字:
print(“如果捕获到异常后,处理”)
例如:
try:
open("xxx.txt")
print(num)
print("----11-----")
except NameError:
print(“如果捕获到异常后,处理...”)
except FileNotFoundError:
print("文件不存在")
except Exception:
print("如果用了Exception,那么意味着只要上面的except没有捕获到异常,那么其他任何异常都会被Exception捕获到")
else:
print("没有异常才会执行的功能")
finally:
print("finally")#不论有没有异常都会执行的代码
"""
except Exception as ret:#将异常名字 赋值给 ret
print("如果用了Exception,那么意味着只要上面的except没有捕获到异常,那么其他任何异常都会被Exception捕获到")
print(ret) #将异常的名字输出
except (NameError,FileNotFoundError):
print("出现异常")
如果想用一个except 捕获多个异常,必须用元组
"""
print("----22---")
class ShortInputException(Exception):#自定义异常类,继承自Exception类
def __init__(self,length,atleast):
self.length = length
self.atleast = atleast
def main():
try:
s = input("请输入 ->>>")
if len(s) < 3:
#raise 引发一个你定义的异常
raise ShortInputException(len(s),3) #类名(参数) 相当于抛出一个对象的首地址
except ShortInputException as result:
print('ShortInputException:输入长度为%d,长度至少为%d'%(result.length,result.atleast))
else:
print("没有异常发生")
class Test(object):
def __init__(self,switch):
self.switch = switch #开关
def calc(self,a,b):
try:
return a/b
except Exception as result:
if self.switch:
print("捕获开启,已经捕获到了异常,信息如下:")
print(result)
else:
raise #表示重新抛出这个异常
a = Test(True)
a.calc(11,0)
print("---------华丽的分割线---------")
a.switch = False
a.calc(11,0)
if "":
if None:
if 0:
if []:
if {}:
这些都相当于False
if 1:
if -1: #数字0表示假,非零都表示真
if "a":
这些都相当于True