文章目录
一、静态方法和类方法
1.1 静态方法
- 通过装饰器@ststicmethod来进行装饰。静态方法即不需要传递对象也不需要传递实例对象
- 静态方法也可以通过实例对象和类对象去访问
class Dog:
type = "狗"
def __init__(self):
name = None
#静态方法
@staticmethod
def introduce(): #静态方法不会自动传递实例对象和类对象
print("犬科哺乳动物,属于肉食目")
dog = Dog()
Dog.introduce()
dog.introduce()
静态方法时类中的函数,不需要实例
静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,也就是说,在静态方法中,不会涉及类中属性和方法的操作
也可以理解为,静态方法是一个独立的,单纯的函数,仅仅是托管于某个类的名称空间中,便于维护和管理
使用场景
- 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
- 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
- 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。
1.2 类方法
- 类对象所拥有的方法
- 需要用装饰器@classmethod来标识其为类方法
- 定义类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class Dog():
__type = "狗"
#类方法,用class来进行装饰
@classmethod
def get_type(cls):
return cls.__type
print(Dog.get_type())
使用场景
- 当方法中需要使用类对象(如访问私有属性时等)时,定义类方法
- 类方法一般和类属性配合使用
注意
类中定义了同名的对象方法,类方法以及静态方法时,会优先执行最后定义的方法
class Dog():
def demo_method(self):
print("对象方法")
@classmethod
def demo_method(cls):
print("类方法")
@staticmethod
def demo_method(): #最后被定义,调用时优先执行
print("静态方法")
dog = Dog()
Dog.demo_method()
dog.demo_method()
二、property
概述:在python中主要为属性提供一个便利的方式
例子
如果我们现在需要设计一个银行账户类,这个类包含账户人的姓名,余额(假如现在不考虑具体的接口操作)
简单的实现
class Account():
def __init__(self,name,money):
self.name = name
self.money = money
问题:不安全,设计很简单方便,但所有的属性都可在外部修改
- 改进1:隐藏实现的细节
对于账户而言,金额不允许让用户直接修改。如果修改,只能去窗口办理。
在使用对象时,尽量不要让使用者直接操作对象中的属性,因为直接操作会带来安全隐患。
这个时候,考虑私有属性
class Account():
def __init__(self,name,money):
self.__name = name
self.__money = money
代码改进以后,将所有的属性设计成私有属性后。确实从外部使用时,不知道内部的属性,不能直接修改对象,隐藏了实现的细节
问题:确实需要对这两个属性进行修改时无法修改
- 改进2:提供一个精确的访问
添加方法,访问私有属性
class Account():
def __init__(self,name,money):
self.__name = name
self.__balance = money
def get_name(self):
return self.__name
def set_balance(self,money):
self.__balance = money
def get_balance(self):
return self.__balance
经过修改,外部使用这个类的对象时,想使用对象中的属性,只能通过类中提供的 set/get 接口来操作,提高了程序的安全性。
这样,程序基本达到了设计需求,但是能不能更加完善呢?
如果在使用这个类的对象过程中,由于误操作,传入了不正常的数据,导致数据异常。该如何以避免这种情况发生呢?
比如:设置金额时出现了负数,或字符串,或其它类型的对象。
- 改进3:保证数据的有效性
set_balance方法中,对传入的数据进行有效性判断,如果无效,返回提示
class Account():
def __init__(self,name,money):
self.__name = name
self.__balance = money
def get_name(self):
return self.__name
def set_balance(self,money):
if isinstance(money,int):
if money > 0:
self.__balance = money
else:
raise ValueError("输入的金额不正确")
else:
raise ValueError("输入的金额不是数据")
def get_balance(self):
return self.__balance
经过几个版本的迭代,程序看起来越来越健壮,安全性能也越来越高
但在使用过程中,可不可以更精炼?
考虑属性操作
使用
在python中,提供了一个property类,通过对创建这个类的对象的设置,在使用对象的私有属性时,可以不再使用属性的函数的调用方式,而是像普通的公有属性一样去使用属性,为开发者提供便利
property(fget=None,fset=None,fdel=None,doc=None) #property attribute
property 是一个类,
__init__
方法由四个参数组成,实例后返回一个用来操作属性的对象
- fget:属性的获取方法
- fset:属性的设置方法
- fdel:实现的删除方法
- doc:属性描述
通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节 。
class Account():
def __init__(self,name,money):
self.__name = name
self.__balance = money
def __get_name(self):
return self.__name
def set_balance(self,money):
if isinstance(money,int):
if money > 0:
self.__balance = money
else:
raise ValueError("输入的金额不正确")
else:
raise ValueError("输入的金额不是数据")
def get_balance(self):
return self.__balance
#使用property类来为属性设置便利的访问方法
name = property(__get_name)
balance = property(get_balance,set_balance)
ac = Account("Tom",10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
# Tom
# 10000
# 1000
改写@property
一个property对象包含三个方法:getter, setter, deleter,当一个函数被@property装饰器修饰时,系统会自动创建一个包含对应访问函数的同名属性
class Account():
def __init__(self, name, money):
self.__name = name
self.__balance = money
@property
def name(self):
return self.__name
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, money):
if isinstance(money, int):
if money > 0:
self.__balance = money
else:
raise ValueError("输入的金额不正确")
else:
raise ValueError("输入的金额不是数据")
ac = Account("Tom", 10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
三、self
如果对象的方法中需要使用该对象的属性,该怎么办?
- 关键字
self
主要用于对象方法中,表示调用该方法的对象 - 在方法中使用self,可以获取到调用当前方法的对象,进而获取该对象的属性和方法
调用对象的方法时,为什么不需要设置self对应的参数?
- 某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给方法,所有开发者只需在定义的时候预留第一个参数
self
即可
class Cat:
# 方法
def introduce(self):
print("name is %s,age is %d"%(self.name,self.age))
#实例化
cat = Cat()
cat.name = "haha"
cat.age = 6
cat.introduce()
#创建另外一个对象
cat2 = Cat()
cat2.name = "xixi"
cat2.age = 2
cat2.introduce()
方法内定义属性
- 使用self操作属性和对象的变量名在效果上类似。如果属性在赋值是还没有被定义,就会自动定义一个属性并赋值
class Cat:
def introduce(self):
self.type = "小型动物"
cat = Cat()
cat.introduce()
print(cat.type)
四、__new__
方法
- 创建对象时,系统会自动调用
__new__
方法 - 开发者可以使用
__new__
方法来自定义对象的创建过程 __new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供__new__
必须要有返回值,返回实例化出来的实例,这点在自己实现__new__
时要特别注意,可以return父类__new__
出来的实例,或者直接是object的__new__
出来的实例__init__
有一个参数self,就是这个__new__
返回的实例,__init__
在__new__
的基础上可以完成一些其它初始化的动作,__init__
不需要返回值- 如果创建对象时传递了自定义参数,且重写了
__new__
方法,则__new__
也必须 “预留” 该形参,否则__init__
方法将无法获取到该参数
class A(object):
def __new__(cls, x):
print('this is in A.__new__, and x is ', x)
return super(A, cls).__new__(cls)
def __init__(self, y):
print('this is in A.__init__, and y is ', y)
class C(object):
def __new__(cls, n):
print('this is in C.__new__, and n is ', n)
return super(C, cls).__new__(cls)
def __init__(self, a):
print('this is in C.__init__, and a is ', a)
class B(A):
def __new__(cls, z):
print('this is in B.__new__, and z is ', z)
return A.__new__(cls, z)
def __init__(self, m):
print('this is in B.__init__, and m is ', m)
# class B(A):
# def __new__(cls, z):
# print('this is in B.__new__, and z is ', z)
# return object.__new__(cls)
# def __init__(self, m):
# print('this is in B.__init__, and m is ', m)
a = A(100)
print('=' * 20)
b = B(200)
print(type(b))
五、__call__
方法
对象后面加括号,触发执行
构造方法的执行是由创建对象触发的,即:对象=类名()
而对于__call__
方法的执行是由对象后加括号触发的,即对象()或者类()
class A(object):
def __call__(self, x):
print('__call__ called, print x: ', x)
a = A()
a('123')
六、__doc__
方法
返回注释信息
class Foo():
'''这是一段说明'''
pass
class Bar(Foo):
pass
print(Foo.__doc__)
print(Bar.__doc__) #该属性无法继承给子类