了解基础魔法函数学会封装和继承&新建模块和函数使用&异常

2023-12-26 10:38:32

一、魔法函数

1.1、概念:

魔法函数(magic methods)是指以双下划线开头和结尾的特殊方法,用于实现对象的特定行为和操作。这些魔法函数可以让我们自定义对象的行为,例如实现对象的比较、算术运算、属性访问等。常见的魔法函数包括__init__(构造函数)、__str__(返回对象的字符串表示)、__add__(实现对象的加法运算)、__eq__(实现对象的相等比较)等。通过实现这些魔法函数,我们可以让我们的自定义对象更加灵活和强大。

原本没有这个能力,把这个函数写出来就有了

1.2、比较操作

  • lt(self, other)定义小于号的行为:x < y

???比大小

  • ???def __lt__():


??# 比大小
? ?def __lt__(self, other):
? ? return self.age < other.age
??
?# 测试lt
?d1 = Dog1(age=20)
?d2 = Dog1(age=18)
?d3 = Dog1(age=19)
?print(d1 < d2) ?# 答案False
  • le(self, other)定义小于等于号的行为:x <= y

  • eq(self, other)定义等于号的行为:x == y

  • ne(self, other)定义不等号的行为:x != y

  • gt(self, other)定义大于号的行为:x > y

  • ge(self, other)定义大于等于号的行为:x >= y

1.3、常见的魔法函数

  • 初始化def __init__():?

?class Dog:
? ? ?namee: str
? ? ?age: int

??
? ? ?
?# 初始化(相当于Java里面的构造函数)
? ?def __init__(self, namee='hhh', age=18):
? ? ? ?self.namee = namee
? ? ? ?self.age = age


 # 测试init传不传参数
 #new一个出来Dog
? d1 = Dog('jack',19)#必须传两个参数,因为初始化上有两个,否则报错

 #如果只想传一个就用默认值
? d1.Dog(age=20)#传了一个它自己self
??

通过关键字传参,指定传参?


?d1 = Dog1(age=20)
?print(d1) ?# 是对象@。。。,显示用tosting一样?

输出对象?

没有重载 ,如果即想要有参有想要无参,只能传默认值(希望某个值不填)

 ?class Dog:
? ? ?name: str
? ? ?age: int


#不能前面有后面没有(name和age都要有)
def __init__(self, name='', age=18):
? ? ? ? ?self.name = name
? ? ? ? ?self.age = age

d1=Dog()

必须带参数因为初始化上有两个,否则报错?

  • def __str__(self):?

???相当于toString
??

 ?class Dog:
? ? ?name: str
? ? ?age: int
?

??def __str__(self):
?????????return f'{self.namee},{self.age}'


# 测试str
d1 = Dog(age=20)
print(d1)  # 是对象@。。。,显示用tosting一样

  • ???def __bool__(self):

??bool 可用于逻辑判断部分

?
??
? ?def __bool__(self):
??? ? ?return self.age > 18
? ? ? ?
? ? ?
?#测试bool
?print(bool(T(17) and T(19))) ?

?

  • ?def __call__():

允许一个类的实例像函数一样被调用,可以把类当成装饰器使用

class Dog:
??def __call__(self, *args, **kwargs):
? ? ? ?print("类的实例可以像函数一样被调用")

?# 测试call
? d1=Dog1(age=20)
#多了个括号
?d1()


  • ?????def __del__(self):

??del 是析构方法,当对象的引用计数变为0时,这个对象会被销毁掉,可以用用于关闭资源



? #del 是析构方法,当对象的引用计数变为0时,这个对象会被销毁掉,可以用用于关闭资源
? ? ?def __del__(self):
??? ? ? ?print("对象被销毁") ? ? ?
? ? ? ? ?
?#测试del
?t = Dog1()
?# t指向0,此前创建的T的实例被销毁
?t = 0
? ? ? ? ?
??
  • ?????def __len__(self):?

?len 是内置函数,len(t) 会调用实例的 len 方法,len一般用在集合中,如果想测量狗,就要重写len

class Dog:
def __len__(self):
return 100

d1 = Dog()
print(len(d1))

1.4、属性管理

属性寻找规则: 先找实例的 dict ,再去查找类的 dict

class Plane(object)?:
   #类属性
   categoryf='飞机'

# 实例化两个飞机,categoryf飞机种类
?p1, p2 = Plane('ccc'), Plane('bb')
 #把类改了
?Plane.category = '拖拉机'
?print(f'{p1.category},{p2.category}')
#输出:拖拉机,拖拉机

 #p1重新赋值
?p1.category = '小飞机'
?print(f'{p1.category},{p2.category}')
??#输出:小飞机,拖拉机

 #p1删除
?del p1.category
?print(f'{p1.category},{p2.category}')
 #输出:拖拉机,拖拉机



???原因:
?# 类对象里面有一个字典
?print(Plane.__dict__)
?# 实例化对象也有一个字典
?print(p1.__dict__)


?? ?总:两个里面都有category这个属性,优先拿自己的(在没有别人的情况下)

1.5、类属性与实例属性区分

class Plane(object)?:
   #类属性
   categoryf='飞机'



def __init__(self, name):
       #实例属性
? ? ? ? ?self.name = name
? ? ? ? 

?属性装在字典里面,所有还能例外方式赋值

把age直接给字典

p1.__dit__['age']=10

?python中对象的属性为什么可以动态变化?

类属性同样是存储在字典中,只是这个字典是类的字典 可以通过类名和对象名来调用,但是只能通过类名修改,类属性对于该类对象是共享的 类的属性和方法存在在类的 dict 之中

二、封装

2.1、概念:

(对属性做限制,封装若有若无)

使用一个下划线就等于向使用者宣布这个属性是私有的,但你仍然可以直接对其修改,单个 下划线更像是一种约定,而非技术上的强硬限制。即便使用了双下划线,也仍然有办法直接对其修改, 但这已经不是类所要解决的问题了,一个人执意破坏数据,他总是能找到办法。

?__强制

2.2、封装方式

封装方式一:?

?class Cat:
? ? ?#私有化
? ? ?__age: int ?# __强制
??
? ? ?def __init__(self, age):
? ? ? ? ?self.__age = age

? ? ? ? ?? ?
? ? ? ? ? ? ?
?#会找不到,因为私有化了,要get set
?c1 = Cat(19)
?print(c1.__age)也不能这样调


?#正确调法
?c1.set_age(30)
?print(c1.get_age())
??   
??
?#方法一
? #get set
? def get_age(self,age):
? ? ? return ?self.__age
??
? def set_age(self,age):
? ? ? ?if age >20:
? ? ? ? ? ? print('死了')
? ? ? ?else:
? ? ? ? ? ? self.__age=age
? ? ? ? ? ? ?
??
?#测试 ?没有get和set强行修改,也可以修改的__dict__
?c1 = Cat(19)
 #直接改字典
?print(c1.__dict__)
??

封装方式二

?
??
?# 封装方法2(装饰器)
?class Pig:
? ? ?#私有化
? ? ?__age: int ?# __强制
??
? ? ?def __init__(self, age):
? ? ? ? ?self.__age = age
??
??
??   #装饰器
? ? ?@property
? ? ?def age(self):
? ? ? ? ?return self.__age
??
??
? ? ?@age.setter
? ? ?def age(self,age):
? ? ? ? ?self.__age= age
? ? ? ? ?

?

? ? ? ? ?
? ? ? ? 
??


?

?两个封装区别

方式一如果想添加代码就要一个一个加,因为不同地方调用了那个方法,方式二就不用
??使用 @property 装饰器修饰 方法后,你就可以像使用属性一样使用 某个属性? ,如果你希望可以对 某个属性进行赋值,那么需要用 @ 某个属性.setter 装饰器再装饰一个方法,该方法完成对 属性的赋值操 作。

我们认为自己直接操作了对象的属性,但其实我们是在使用类的方法,而且关键的是省去了调用方法时 的那对小括号
???

2.3、方法拓展

? 
? 类方法可以用类名调用
? ?#类方法
? ? ?@classmethod
? ? ?def run(cls):
? ? ? ? ?#可以操作类属性,不能操作对象属性因为没有self
? ? ? ? ?print(cls.__age)
#用类调用

?Pig.run()


? ? ?#静态方法,什么东西都没带,不能类属性也不能调对象属性,只是单纯函数而已
? ? ?@staticmethod
? ? ?def say():
? ? ? ? ?print("hello world")
? ? ? ? ?

三、继承

3.1、概念

如果没有指定基类,会默认继承 object 类, object 是所有类的基类,它提供了一些常见方法

3.2、语法

class Dog(多个父类,就进原则,如果是obj可以默认不写):

3.3、区别

类属性Dog.xx 实例属性self.xx

类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

class Dog:
    pass

class Cat:
    pass


#一个父亲一个母亲
class Lion(Dog, Cat):
    pass

?查看继承谁

#方式一:__bases__?
print(Lion.__bases__)

#方式二:__mro__?更详细
?print(Lion.__mro__)

跟谁亲,就近原则?

场景:在一个类要实现多个类,可以产生方法冲突

class Lion(Dog, Cat):
    pass

?
class Dog:
? ? ?food:'叭叭叭'
? ? ?def say(self):
? ? ? ? ?print('我要吃'+self.food)
??
??
?class Cat:
??
? ? ?food='yyyyy'
??
? ? ?def say(self):
? ? ? ? ?print('我要吃' + self.food)
??

#希望调父类方法:

?#调用错误的 ----不能用类去调,say是对象
?print(Lion.say())

?#正确的
?Lion().say()
?print(Lion.food)
??

四·、多态

4.1、概念:

在一个时期有不同的状态

#动物类
class Animal(object):
    #表演的方法
    def play(self):
        pass

#老虎类继承了动物类
class Tiger(Animal):
    #重写方法
    def play(self):
        print("正在表演老虎吃人")

#狮子类继承了动物类
class Lion(Animal):
    def play(self):
        print("正在表演狮子跳火圈")

#人类与
class Person(object):
    #与动物玩耍
    def show(self, a: Animal):
        print("动物开始表演了")
        a.play()


p = Person()
#一个老虎
tiger = Tiger()
#一个狮子
lion = Lion()

谁表演

# 让老虎表演
p.show(tiger)
# 让狮子表演
p.show(lion)

五、模块(Python文件)

在Java习惯:不同作用的类放在一起,形成包 --->导包

?导入模块=相当于Alt+/

5.1、概念:

Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。 模块让你能够有逻辑地组织你的 Python 代码段。 把相关的代码分配到一个模块里能让你的代码更好用,更易懂。 模块能定义函数,类和变量,模块里也能包含可执行的代码

5.2、语法:

? import ?modu

5.3不同方式导模块

?#方法一导入全部方法 取个别名
? import ?modu as m
? m.p()
?m.hello()
??
?#方法二单独某个方法
? from modu import hello,P
? hello()
? P()
?#方法三单独一个方法取个别名
? from modu import ?hello as h
? h()
??
?#方法四导全部,但是是自己想要给别人的全部
? from modu import *
? P()
?搭配 __all__ = [] 使用



#另外一个页面
 __all__ = ['Cat']

Class Cat:
    pass

def hello():

5.4、导入包

?import package.module
?import package.module as xx
?from package.module import xx
?from package.module import xx as xx
??
??
?#导包
?#方法一要 包名点类名取别名
? import soud.s1 as s
? s.say1()
??
?#方法二要在init里面写all
?from soud import ?*
?s1
?搭配 __init__.py中的__all__ = [] 使用

六、标准模块collections

collections实现了许多特定容器,这些容器在某些情况下可以替代内置容器 dict, list, tuple, set, 原因 在于,他们提供了在某个方面更加强大的功能。

6.1、Counter 统计对象的个数

数据太多,可以计算它每个字母出现的次数

?keys = "Hold fast,and let go;" \
????"understand this paradox," \
????"and you stand at the very gate of wisdom."
?cs = Counter(keys)
?print(cs)

\回车代表换行

6.2、ChainMap 合并多个字典,还可以用**

?dic1 = {'python': 100}
?dic2 = {'java': 99}
?cm = ChainMap(dic1, dic2)
?cm = dict(cm)#转字典
?print(cm)

6.3、OrderedDict 有序字典 (3.6之后字典自带)

6.4、defaultdict默认字典

不会引发KeyError的字典,如果拿一个没有的会报错,它可以使它不报错返回0 在创建这种字典时,必须传入一个工厂函数,比如int, float, list, set。defaultdict内部有一个 missing(key) 方法,当key不存在时,defaultdict会调用 missing 方法根据你所传入的工厂函数返回一个默认值。你传入的工厂函数是int,那么返回的就是0, 如果传入的是list,返回的就是空列表

?d1={
? ? "java":100,
? ? "python":99
?}
?print(d1['c++'])#没有c++这个课程,报错,但是有时候我们不希望它报错


??
?eg:
 #指定类型
?dic = defaultdict(int)
?dic['a'] = 1
?dic['b'] = 2
?dic['c'] = 3
?print(dic['f'])

6.5、deque 一个类似列表的容器

栈就相当于客栈:先进后出

队列相当于排队:先进先出

方法

  • 类似列表的容器,支持在两端快速的追加和删除元素。

  • append (在末尾追加数据)

  • appendleft (在头部追加数据)

  • pop (删除并返回指定索引的数据,默认是末尾数据)

  • popleft(删除并返回头部数据)

  • insert(在指定位置插入数据)

  • remove(删除指定数据)

七、functools函数工具

7.1、singledispatch 单处理的泛型函数(单个调动)

?#以前写法
#学生类
?class Stu(object):
??def wake_up(self):
????print('起床')
??
#警察类
?class Police:
??def wake_up(self):
????print('起床')

#new了一个学生一个警察??
?stu = Stu()
?police = Police()

??
?def wake_up(obj):
#isinstance判断某个类是不是它的子类
??if isinstance(obj, Stu):
????print('今天周末休息,让孩子们再睡一会')
??elif isinstance(obj, Police):
????print('警察很辛苦,又要起床了')
????obj.wake_up()
??else:
????print('不处理')
??
?wake_up(stu)
?wake_up(police)
?wake_up('一个字符串')
??



?#现在写法
??
?
?from functools import singledispatch
??
?class Stu(object):
??def wake_up(self):
????print('起床')
??
?class Police:
??def wake_up(self):
????print('起床')
??
?stu = Stu()
?police = Police()
??

#装饰器
?@singledispatch
?def wake_up(obj):
??print('不处理')
??
?@wake_up.register(Stu)
?def wake_stu(obj):
??print('今天周末休息,让孩子们再睡一会')
??
?@wake_up.register(Police)
?def wake_police(obj):
??print('警察很辛苦,又要起床了')
??
??obj.wake_up()
?wake_up(stu)
?wake_police(police)
?wake_up('一个字符串')

7.2、wraps 装饰器修饰

python标准模块functools提供的wraps函数可以让被装饰器装饰以后的函数保留原有的函数信息,包括 函数的名称和函数的注释doc信息。

#做成了装饰器
def hello(func):
    #加了这个装饰器以后输出say
    @wraps(func)
    def inner():
        pass

    return inner



@hello
def say():
    pass


#输出inner,就比如是答非所问了
point(say. __name__)

7.3、reduce 规约函数

reduce(对集合做操作)的作用是从左到右对一个序列的项累计地应用有两个参数的函数,以此合并序列到一个单一值,简单来说就是把集合合成一个整体

与filter()过滤、map()一样的集合做操作,reduce要导包

计算1-100的和


?reduce(lambda a,b:a+b,range(1,100))#1,2,3,4....
??
?#a第一次是1,b为2 a+b=3
?#a第二次是3 b是后面那个就是3
??

??

相加起来拼成一个字符串

?ns=[{"name":"a"},{"name":"b"},{"name":"c"}]
?#字符串后面加个空字符串
?print(reduce(lambda a,b:a+b['name'],ns,""))
?#int后面加个0
?print(reduce(lambda a,b:a+b['age'],ns,0))

八、异常

8.1、.抓异常try

?try:
?  ?print(1 / 0)
?except Exception as e:#分母不能为0
?  ?print(e)
?else:
??print('no error')#没异常出
?finally:
??print('close')#不管有没有异常都出来

8.2、自定义异常类 raise抛出异常


?try:
??raise TypeException('类型不匹配')
?except Exception as e:
??print(e)
??
??
??
?def eat(obj):
? ? ?#方法一raise
? ? ?if isinstance(obj,str):
? ? ? ? ?pass
? ? ?else:
? ? ? ? ?raise Exception("类型不是我想要的")
? ? ? ?#方法二断言assert
? ? ?assert isinstance(obj str),"类型不符合"
? ? ?
?eat(123) ? ? ? ?

文章来源:https://blog.csdn.net/m0_74276368/article/details/135191936
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。