python3基础知识复习 -- 面向对象编程多态,BIF,修饰符

用实例解释Python中的继承和多态的概念 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self): print 'Animal is running...' 当我们需要编写Dog和Cat类时,就可以直接从Animal类继承: class Dog(Animal): pass class Cat( 立即下载

多态

  • 当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。
class Animal(object):
    def run(self):
        print('Animal is running...')
        
class Dog(Animal):
    def run(self):
        print('Dog is running...')

class Cat(Animal):
    def run(self):
        print('Cat is running very fast...')
  • 多态的好处

    # 定义一个run_twice函数
    def run_twice(animal):
        animal.run()
        animal.run()
        
    # 当我们传入Animal的实例时,run_twice()就打印出:
    >>> run_twice(Animal())
    Animal is running...
    Animal is running...
    
    # 当我们传入Dog的实例时,run_twice()就打印出:
    >>> run_twice(Dog())
    Dog is running...
    Dog is running...
    
    # 当我们传入Cat的实例时,run_twice()就打印出:注意直接调用的Cat的run的方法
    >>> run_twice(Cat())
    Cat is running very fast...
    Cat is running very fast...
    
  • 这就是多态的意思:

    对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在AnimalDog、还是Cat对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

    对扩展开放:允许新增Animal子类;

    对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

静态语言 vs 动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。

在程序设计中,**鸭子类型(duck typing)**是动态类型的一种风格。

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。

James Whitcomb Riley 提出的鸭子测试,“鸭子测试”可以这样表述:

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性(Python 文档中有一句:鸭子类型应避免使用 type() 或 isinstance() 等测试类型是否合法)。

	class Duck:
	    def quack(self): 
	        print("呱呱呱!")
	    def feathers(self): 
	        print("这个鸭子拥有灰白灰白的羽毛。")
	
	class Person:
	    def quack(self):
	        print("你才是鸭子你们全家人是鸭子!")
	    def feathers(self): 
	        print("这个人穿着一件鸭绒大衣。")
	
	def in_the_forest(duck):
	    duck.quack()
	    duck.feathers()
	
	def game():
	    donald = Duck()
	    john = Person()
	    in_the_forest(donald)
	    in_the_forest(john)
	
	game()

从哪里可以看出 Python 是鸭子类型的风格呢?

in_the_forest() 函数对参数 duck 只有一个要求:就是可以实现 quack() 和 feathers() 方法。然而 Duck 类和 Person 类都实现了 quack() 和 feathers() 方法,因此它们的实例对象 donald 和 john 都可以用作 in_the_forest() 的参数。这就是鸭子类型。

我们可以看出,鸭子类型给予 Python 这样的动态语言以多态。 但是这种多态的实现完全由程序员来约束强制实现(文档、清晰的代码和测试),并没有语言上的约束(如 C++ 继承和虚函数)。 因此这种方法即灵活,又提高了对程序员的要求。

类,类对象,实例对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ALkaHdSh-1651018619095)(…/resources/image51-9.png)]

  • 实例对象属性赋值不会改变类对象的值,类一旦定义就会产生类对象
class A:
	num = 10

###### 

a1 = A()
a2 = A()
a3 = A()

a3.num += 10 --- 实例对象的num属性变化,不影响其他实例对象,也不会影响类对象的,因为他们在不同的内存空间
a3.num
20

a1.num
10

a2.num
10

A.num
10

A.num += 100 --- 类对象的num属性变化,导致其他未改变的实例对象相应变化但不会影响已经变化的
a1.num
110

a2.num
110

a3.num
20

  • 千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
  • 如果属性和方法重名,则会覆盖方法
    >> 不要试图在一个类里面定义所有能想到的特性和方法,应该利用继承和组合机制类进行扩展.
    >> 用不同的词性命名,如属性用名词,方法用动词**
class C:
	def x(self):
		print("X-Ray!")

###### 
c1 = C()
c1.x()
X-Ray!

c1.x = 1 --- 对象c1产生一个新的x属性(python中变量不用声明)
c1.x
1

c1.x() --- 由于c1的x属性和类的x方法同名,调用的时候直接找c1对象去了,就会报错
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
c1.x()
TypeError: 'int' object is not callable
  • 绑定:python 严格要求方法需要有实例才能被调用,这种限制其实就是python所谓的绑定概念
    使用实例属性(self.x, dd.dict),不要用类属性, 类被删除,实例依然可以调用属性(因为类的属性和方法是静态的存放在内存中)
class A:
	def __init__(self, x):
		self.x = x

	def getX(self):
		return self.x

###### 

class B(A):
	pass

###### 

A(0).x
0

b2 = B(1)
b2.x
1

b2.getX()
1

A.dict (查看该类的方法,字典格式,键值是属性或者方法,单引号加持)
mappingproxy({'module': 'main', 'init': <function A.init at 0x00000202A1FDF160>, 'getX': <function A.getX at 0x00000202A1FDF1F0>, 'dict': <attribute 'dict' of 'A' objects>, 'weakref': <attribute 'weakref' of 'A' objects>, 'doc': None})

b2.dict (b2实例化的属性/方法只有x,因为b2实例化的时候x和b2是绑定的)
{'x': 1}

del A (此时删除类A)

a3 = A()
Traceback (most recent call last):
File "<pyshell#63>", line 1, in <module>
a3 = A()
NameError: name 'A' is not defined

b2.getX() --(b2依然可以正常使用属性/方法)
1

b2.x
1

类和对象的一些相关的BIF

*issubclass(class, classinfo)— 如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回 True,否则返回 False。

另外以下这些常识你应该知道:

  • 一个类被认为是其自身的子类
  • classinfo 可以是类对象组成的元祖,只要 class 与其中任何一个候选类的子类,则返回 True
  • 在其他情况下,会抛出一个 TypeError 异常
 class A:
	pass

class B(A):
	pass

issubclass(B, A)
True

issubclass(B, B)
True

issubclass(B, C)
False

issubclass(B, (A, C))
True

*isinstance(object, classinfo)— 如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回 True,否则返回 False。

另外以下这些常识你应该知道:

  • 如果 object是 classinfo 的子类的一个实例,也符合条件
  • 如果第一个参数不是对象,则永远返回False
  • classinfo 可以是类对象组成的元祖,只要class与其中任何一个候选类的子类,则返回 True
  • 如果第二个参数不是类或者由类对象组成的元祖,会抛出一个 TypeError 异常.
 a = A()

isinstance(a, A)
True

isinstance(a, B)
False

isinstance(a, 'a')

Traceback (most recent call last):
File "<pyshell#103>", line 1, in <module>
isinstance(a, 'a')
TypeError: isinstance() arg 2 must be a type or tuple of types

另外有type() 方法:

*type() — 看元素类型

  • 如果我们要在if语句中判断,就需要比较两个变量的type类型是否相同

  • >>> type('abc') == type('123')
    True
    
  • 判断基本数据类型可以直接写intstr等,但如果要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量:

    >>> import types
    >>> def fn():
    ...     pass
    ...
    >>> type(fn)==types.FunctionType
    True
    >>> type(abs)==types.BuiltinFunctionType
    True
    >>> type(lambda x: x)==types.LambdaType
    True
    >>> type((x for x in range(10)))==types.GeneratorType
    True
    
  • 能用type()判断的基本类型也可以用isinstance()判断,总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。

    # 可以判断是是否是某些类型中的一种
    >>> isinstance([1, 2, 3], (list, tuple))
    True
    

type() 隐藏技能

  • 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

  • type()函数可以查看一个类型或变量的类型,B是一个class,它的类型就是type,而b是一个实例,它的类型就是class

    >>> class B:
    ...     pass
    ... 
    >>> type(B) 
    <class 'type'>
    >>> b = B()
    >>> type(b)
    <class '__main__.B'>
    
  • 我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数, 无需使用class xxx

    要创建一个class对象,type()函数依次传入3个参数:

    1. class的名称;
    2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名study上。

    通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

    >>> def fn(self, name='day day up'): # 定义一个函数
    ...     print('good good study, %s' %name)
    ... 
    >>> C = type('C', (object,), dict(study=fn)) # type()动态创建类
    >>> c = C()
    >>> c.study()
    good good study, day day up
    >>> type(C)
    <class 'type'>
    >>> type(c)
    <class '__main__.C'>
    

*dir() – 获得一个对象的所有属性和方法,它返回一个包含字符串的list

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

操作对象状态:

*hasattr(object, name)-- 测试一个对象object是否有指定的属性(name),注意属性用字符串表示需要加引号

*getattr(object, name[, default])— 得到一个对象object的指定属性name的值,如果没有设置default参数则返回attributeERROR, 否则返回default内容

class C:
	num = 3

c = C()

hasattr(c, num) #不加引号一定会报错
Traceback (most recent call last):
File "<pyshell#108>", line 1, in <module>
hasattr(c, num)
NameError: name 'num' is not defined

hasattr(c, 'num')
True

hasattr(c, 'number')
False

getattr(c, 'num')
3

getattr(c, 'number', 'Not valid') #当属性不存在时给出一个默认值
'Not valid'

# 假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。
def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

也可以获得对象的方法:

>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

*setattr(object, name, value)— 为对象object设置属性name的值,如果属性不存在则新建这个属性并赋值value

*delattr(object, name)— 删除对象的属性name,若属性不存在则抛出AttributeError异常

*property(fget=None, fset=None, fdel=None, doc=None)-- 通过属性设置属性,对用户方便,只需要提供x的接口,以后改方法的时候方便

class property(object)
	property(fget=None, fset=None, fdel=None, doc=None)
	Property attribute.
|
| fget
| function to be used for getting an attribute value
| fset
| function to be used for setting an attribute value
| fdel
| function to be used for del'ing an attribute
| doc
| docstring


Typical use is to define a managed attribute x:
class C(object):
	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.")

# example:
class Size:
	def __init__(self, size=10):
		self.size = size

	def getSize(self):
		return self.size

	def setSize(self, value):
		self.size = value

	def delSize(self):
 		del self.size

	x = property(getSize, setSize, delSize)

###### 

small = Size()
small.x
10

small.x = 20
small.x
20

small.getSize()
20

del small.x
small.x
Traceback (most recent call last):
File "<pyshell#150>", line 1, in <module>
small.x
File "<pyshell#143>", line 5, in getSize

return self.size
AttributeError: 'Size' object has no attribute 'size'

函数修饰符(decorator)

  • 函数修饰符
    修饰符是解决这类问题(有插入日志、性能测试、事务处理等)的绝佳设计,有了修饰符,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。
  • 概括的讲,修饰符的作用就是为已经存在的对象添加额外的功能。
  • Python 的修饰符就是一种优雅的封装,但要注意的是只可以在模块或类定义内对函数进行修饰,不允许修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。
Eg:

********************************************
import time
	def timeslong(func):
		start = time.time()
		print("It's time starting ! ")	
		
        func()
		print("It's time ending ! ")
		end = time.time()

        return "It's used : %s." % (end - start)

def myfunc():
	print("Hello FishC.")

t = timeslong(myfunc)
print(t)

********************************************

等价于下面的修饰符形式:

********************************************

import time

def timeslong(func):
	def call():	
		start = time.time()
		print("It's time starting ! ")
		func()
		print("It's time ending ! ")
		end = time.time()
		return "It's used : %s." % (end - start)
	return call

@timeslong
def myfuna():
	print("Hello World.")

@timeslong
def myfunb():
	print("Hello Python.")

@timeslong
def myfunc():
	print("Hello FishC.")

print(myfuna())
print("========================================")
print(myfunb())
print("========================================")
print(myfunc())

********************************************

或者包装成类的形式

********************************************

import time

class timeslong(object):
	def __init__(self, func):
		self.func = func

	def __call__(self):
		start = time.time()
		print("It's time starting ! ")
		self.func()
		print("It's time ending ! ")
		end = time.time()
		return "It's used : %s." % (end - start)

@timeslong
def myfuna():
	print("Hello World.")

@timeslong
def myfunb():
	print("Hello Python.")

@timeslong
def myfunc():
	print("Hello FishC.")

print(myfuna())
print("========================================")
print(myfunb())
print("========================================")
print(myfunc())
  • 这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
>>> def now():
...     print('2015-3-25')

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)  # func.__name__ 表示函数的名称
        return func(*args, **kw)
    return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
    print('2015-3-25')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()
call now():
2015-3-25

@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

  • 如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():
    print('2015-3-25')

执行结果如下:

>>> now()
execute now():
2015-3-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)

我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

或者针对带参数的decorator:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

eg: 设计一个decorator打印一个函数的执行时间

# -*- coding: UTF-8 -*-
import time, functools

# 不加参数装饰器
def metric(fn):

    @functools.wraps(fn)
    def wrapper(*args, **kw):
        t1 = time.time()
        result = fn(*args, **kw)
        t2 = time.time()
        print('function %s run %f ms' % (fn.__name__, t2 - t1))
        return result

    return wrapper


@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y


@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x y z


f = fast(11, 22)
s = slow(11, 22, 33)
print(f, s)

function fast run 0.006482 ms
function slow run 0.138556 ms
33 7986


# 装饰器加不加参数都可运行
# -*- coding: UTF-8 -*-
import time, functools

def log(text):
    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kw):
            if isinstance(text, str):
                print('%s %s' % (text, fn.__name__))
            else:
                print('calling %s' % fn.__name__)
            return fn(*args, **kw)

        return wrapper

    if isinstance(text, str):
        return decorator
    else:
        return decorator(text)


@log
def fast(x, y):
    time.sleep(0.0012)
    return x + y


@log('execute')
def slow(x, y, z):
    time.sleep(0.1234)
    return x y z


f = fast(11, 22)
s = slow(11, 22, 33)
print(f, s)

枚举类

  • Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
  • 当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份,好处是简单,缺点是类型是int,并且仍然是变量。
JAN = 1
FEB = 2
  • 更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:
from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

value属性则是自动赋给成员的int常量,默认从1开始计数。

如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

@unique装饰器可以帮助我们检查保证没有重复值。

访问这些枚举类型可以有若干种方法:

>>> day1 = Weekday.Mon
>>> print(day1)
Weekday.Mon
>>> print(Weekday.Tue)
Weekday.Tue
>>> print(Weekday['Tue'])
Weekday.Tue
>>> print(Weekday.Tue.value)
2
>>> print(day1 == Weekday.Mon)
True
>>> print(day1 == Weekday.Tue)
False
>>> print(Weekday(1))
Weekday.Mon
>>> print(day1 == Weekday(1))
True
>>> Weekday(7)
Traceback (most recent call last):
  ...
ValueError: 7 is not a valid Weekday

>>> for name, member in Weekday.__members__.items():
...     print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat

可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

python内置的修饰符

  • 分别是 staticmethod、classmethod 和 property,作用分别是把类中定义的实例方法变成静态方法、类方法和类属性.
# 类方法 @classmethod
# 只需要在类里面的函数名上方加上一个 @classmethod 修饰符,就可直接使用Hello.print_hello()

>>> class Hello(object):
...     @classmethod
...     def print_hello(cls):
...         print("Hello FishC")
>>> Hello.print_hello()


# 类的静态方法 @staticmethod
# 静态方法是类的特殊方法,静态方法只需要在普通方法的前边加上 @staticmethod 修饰符即可。
# 静态方法最大的优点是:不会绑定到实例对象上,换而言之就是节省开销。使用的时候需要注意的地方:静态方法并不需要 self 参数,因此即使是使用对象去访问,self 参数也不会传进去。

>>> class B:
	@staticmethod
	def checksum(arg1, arg2, arg3):
		return arg1 + arg2 + arg3
	def nocheck(self):
		print('I am X-man!')
>>> b1 = B()
>>> b1.checksum
<function B.checksum at 0x03AF1538>
>>> B.checksum
<function B.checksum at 0x03AF1538> # 注意这个地址和上面b1调用的地址是一样

>>> b1.nocheck
<bound method B.nocheck of <__main__.B object at 0x03AE02E0>>
>>> B.nocheck
<function B.nocheck at 0x03AF1580> # 注意这个地址和上面b1调用的地址不一样

@property(属性修饰符)
  • Python内置的@property装饰器就是负责把一个方法变成属性调用,既能检查参数,又可以用类似属性这样简单的方式来访问类的变量.(类似于函数用的decorator)

  • 使用属性修饰符创建描述符:

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

  • 把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:(Note: 还可以定义只读属性,只定义getter方法,不定义setter方法, 就是一个只读属性了)
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!
  • 要特别注意:属性的方法名不要和实例变量重名。例如,以下的代码是错误的:

    class Student(object):
    
        # 方法名称和实例变量均为birth:
        @property
        def birth(self):
            return self.birth
    
    • 这是因为调用s.birth时,首先转换为方法调用,在执行return self.birth时,又视为访问self的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError

喜欢就记得三连哦 😃


Python3修饰符@ python函数修饰符@的作用是为现有函数增加额外的功能,常用于插入日志、性能测试、事务处理等等。 1、被修饰函数不带参数 def log(func): def wrapper(): print('log开始 ...') func() print('log结束 ...') return wrapper @log def test(): print('test ..') test() 输出 log开始 ... test .. 阅读详情

相关推荐

Python 语言

实战总结

QQ418579986的博客 4141

python @修饰符

Python 除了拥有实例方法外,还拥有静态方法和类方法,跟Java相比需要理解这个类方法的含义。 class Foo(object):      def test(self)://定义了实例方法          print("object")      @classmethod      def test2(clss)://定义了类方法          print("class"

jincm的专栏 3037

python常用修饰器

1. @staticmethod 不需要实例化即可使用class的属性,方法 #!/usr/bin/python # -*- coding: UTF-8 -*- class A(object): bar = 1 def func1(self): print ('foo') @classmethod def func2(cls): print ('func2') print (cls.bar) cls()

tianshuiyimo的博客 1043

@修饰符--python中的装饰器

装饰器模式可以在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责,也能够处理那些可以撤销的职责。经常用于日志记录、性能测试等场合。 想象一下这个很常见的场景,你写了一个方法只提供给以登陆的用户访问(事实上我也是通过django的@login_required才了解到@修饰符的),你可以写以下代码: def A(): if user.is_login():

AncerHaides的专栏 1万+

python修饰符

修饰符基础——闭包 什么是闭包呢?标准的概念大家可以看wikipedia上的解释点击打开链接   举个例子: def do_add(base): def add(increase): return base + increase return add do_add函数里嵌套了一个内层函数add,这个内层函数就是一个闭包,其实可以也不用管这个“闭包”的概

xerxes的专栏 8337

Python3中的self的作用

所谓的self,可以理解为自己可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可self的用法:class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def ...

好好的博客 1万+

Python修饰符 (一)—— 函数修饰符 “@”

今天被问到Python函数修饰符,顺手写写。 Python函数修饰符,“@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。 举个栗子,下面的一段代码,里面两个函数,没有被调用,也会有输出结果: def test(f): print "before ..." f() print "after ..." @test def func(): pri...

Arthur Guo 的专栏 3万+

关于python 的@操作符 (Decorator) 的用法

直接翻译过来应该叫做 装饰符吧,不知是不是python特有的用法,看了下说明,艰难的弄明白了一些运作的原理。 简单的说,@操作符是用来提供调用的,光是说明可能无法理解,写了一个小小的例子: def f1(arg):     print "f1"     rl = arg()     print rl     return rl + "f1"      @f1

dolphin98629的专栏 723

python @修饰符有何用_Python3修饰符,用于方法

正如标题所述,我正在寻找一种修饰类的方法,这样当我调用方法时,decorator应用于该调用。这背后的主要原因是允许我捕捉函数调用及其传递的参数。我之所以不走传统的装饰方法的道路,是因为这些方法可能不会提前知道,因为这些类将通过插件进行扩展。我也对人们可能会遇到的其他实现持开放态度。在from functools import wrapsdef echo(fh):def func_wrapper(...

weixin_39901358的博客 252

学习笔记 - Python3 修饰符 classmethod

关于理解修饰符classmethod的前置知识 按照我个人的理解,我想先阐述类与实例的关系,这样将有助于不清楚类的同学理解classmethod的意图 类的话可以理解为制作物品的模具: 实例的话可以理解为模具制作出来的物品: 可以通过下面的方法鉴别类和实例:我将提供一个简单的代码来鉴别一个实例和类 # 我先编写一段类(创建类的时候括号可以加或不加,如果该类有要继承的父类,就需要把父类写...

weixin_42582160的博客 643

Python 中的@修饰符作用

python 2.4以上的的函数中偶尔会看到函数定义的上一行有@functionName的修饰,这一下这个语法细节,其实这有点像C语言带参数的宏操作,解释器读到这样的修饰之后,会先解析@后的内容,直接就把@下一行的函数或者类作为@后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象。 def funA(a): print 'funA' def funB(b): print

byteway的专栏 7529

python中@用法

@修饰符    ‘@’符号用作函数修饰符python2.4新增加的功能,修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。也就是说@A def f(): 是非法的。 只可以在模块或类定义层内对函数进行修饰,不允许修修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。   本质上讲,装饰符@类似于回调函数,把其它的函数(暂且称为目的参...

Harlan的博客 3791

python3 中@修饰符作用

参考如下链接:https://www.liaoxuefeng.com/wiki/897692888725344/923030163290656

qq_37087409的博客 1014

python中的修饰符(@)

今天在看《opencv3计算机视觉-python语言实现》的第2章中,遇到了一个新的符号“@”,之前一直听说python要学到了修饰符才算入门,今天终于见到庐山真面目啦,哈哈。我是看了这篇文章 ,稍微明白了一点,感觉修饰符有点像“模板函数”,但是好像又比模板函数灵活、强大一些。尤其是那句 通过另外一个函数传递函数对象,然后将结果赋给原来的函数。 有种茅塞顿开的感觉。修饰符的引入可以用来解决 :

u011629133的博客 839

Python3的成员修饰符

定义私有成员: class stu1(): def __init__(self, name, age): self.name = name # self.age = age self.__age = age # 外部无法直接访问 内部可以访问 def getname(self): return self.na...

哎一入江湖岁月催的专栏 1204

python中的函数修饰符“@”

Python函数修饰符,“@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。 举个栗子,下面的一段代码,里面两个函数,没有被调用,也会有输出结果: def test(f): print "before ..." f() print "after ..." @test def func(): ...

jasonLee的博客 1560

高铁动车座椅三维模型:获取、导入、优化与合规应用全指南

三维模型是数字内容创作与工业仿真的基础资产,其核心在于通过多边形网格、材质贴图与骨骼动画等数据,在虚拟空间中构建物体的可视化形态。其工作原理涉及建模、UV展开、纹理映射与渲染管线,旨在平衡视觉真实感与计算性能。这项技术的价值在于能极大提升设计、仿真与内容创作的效率与专业性,避免从零开始的重复劳动。在应用场景上,三维模型广泛应用于工业设计可视化、轨道交通仿真、游戏开发、影视动画及VR/AR体验构建。本文聚焦于高铁动车座椅这一特定模型资源,深入探讨如何获取高质量模型、处理常见的贴图丢失与格式兼容性问题,并重点解

weixin_30251829的博客 349
上一篇: python3基础知识复习 -- 面向对象编程的封装,继承
下一篇: python3基础知识复习 -- 魔法方法之构造,算数,属性定义
peanutfish
博客等级 码龄14年 255粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值