Python中的TensorFlow干嘛的_第2页回答

2020-05-08 11:24发布

11条回答
我的网名不再改
2楼 · 2021-11-10 13:45

虽然用python用了很久了,但是主要还是写一些模型或者算子,对于python中的高级特性用的不多,但是时常阅读大牛的代码或者框架源码,其中python特性应用的非常流畅,所以今天决定与python中的装饰器@,做个了断!!

Python中的@:

援引廖雪峰老师对装饰器的解释以及一些自己对装饰器的理解:

python中在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。@是装饰器的语法。装饰器是在函数调用之上的修饰,这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。 我们可以用装饰器来增加计时逻辑来检测性能,或者引入日志等等。

函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

>>> def now():

... print('2015-3-25')

...

>>> f = now

>>> f()

2015-3-25

函数对象有一个__name__属性,可以拿到函数的名字:

>>> now.__name__

'now'

>>> f.__name__

'now'

现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义。本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):

def wrapper(*args, **kw):

print('call %s():' % 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

import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

python中常见的@:

@property :对于类的方法,装饰器一样起作用,Python内置的@property装饰器就是负责把一个方法变成属性调用的.广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

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> 100:

raise ValueError('score must between 0 ~ 100!')

self._score = value

@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个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!

注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

@staticmethod,@classmethod:@staticmethod返回的是一个staticmethod类对象,而@classmethod返回的是一个classmethod类对象。他们都是调用的是各自的__init__()构造函数。

当然应用装饰器不当也会带来一些问题:

1、位置错误的代码

让我们直接看示例代码。

def html_tags(tag_name):

print 'begin outer function.'

def wrapper_(func):

print "begin of inner wrapper function."

def wrapper(*args, **kwargs):

content = func(*args, **kwargs)

print "{content}{tag}>".format(tag=tag_name, content=content)

print 'end of inner wrapper function.'

return wrapper

print 'end of outer function'

return wrapper_

@html_tags('b')

def hello(name='Toby'):

return 'Hello {}!'.format(name)

hello()

hello()

在装饰器中我在各个可能的位置都加上了print语句,用于记录被调用的情况。你知道他们最后打印出来的顺序吗?如果你心里没底,那么最好不要在装饰器函数之外添加逻辑功能,否则这个装饰器就不受你控制了。以下是输出结果:

begin outer function.

end of outer function

begin of inner wrapper function.

end of inner wrapper function.

Hello Toby!

Hello Toby!

2、错误的函数签名和文档

装饰器装饰过的函数看上去名字没变,其实已经变了。

def logging(func):

def wrapper(*args, **kwargs):

"""print log before a function."""

print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

return func(*args, **kwargs)

return wrapper

@logging

def say(something):

"""say something"""

print "say {}!".format(something)

print say.__name__ # wrapper

为什么会这样呢?想想装饰器的语法@代替的东西就明白了。@等同于这样的写法。

say = logging(say)

logging其实返回的函数名字刚好是wrapper,那么上面的这个语句刚好就是把这个结果赋值给say,say的__name__自然也就是wrapper了,不仅仅是name,其他属性也都是来自wrapper,比如doc,source等等。

使用标准库里的functools.wraps,可以基本解决这个问题。

from functools import wraps

def logging(func):

@wraps(func)

def wrapper(*args, **kwargs):

"""print log before a function."""

print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

return func(*args, **kwargs)

return wrapper

@logging

def say(something):

"""say something"""

print "say {}!".format(something)

print say.__name__ # say

print say.__doc__ # say something

看上去不错!主要问题解决了,但其实还不太完美。因为函数的签名和源码还是拿不到的。

import inspect

print inspect.getargspec(say) # failed

print inspect.getsource(say) # failed

如果要彻底解决这个问题可以借用第三方包,比如wrapt。

3、不能装饰@staticmethod或者 @classmethod

当你想把装饰器用在一个静态方法或者类方法时,不好意思,报错了。

class Car(object):

def __init__(self, model):

self.model = model

@logging # 装饰实例方法,OK

def run(self):

print "{} is running!".format(self.model)

@logging # 装饰静态方法,Failed

@staticmethod

def check_model_for(obj):

if isinstance(obj, Car):

print "The model of your car is {}".format(obj.model)

else:

print "{} is not a car!".format(obj)

"""

Traceback (most recent call last):

...

File "example_4.py", line 10, in logging

@wraps(func)

File "C:\Python27\lib\functools.py", line 33, in update_wrapper

setattr(wrapper, attr, getattr(wrapped, attr))

AttributeError: 'staticmethod' object has no attribute '__module__'

"""

前面已经解释了@staticmethod这个装饰器,其实它返回的并不是一个callable对象,而是一个staticmethod对象,那么它是不符合装饰器要求的(比如传入一个callable对象),你自然不能在它之上再加别的装饰器。要解决这个问题很简单,只要把你的装饰器放在@staticmethod之前就好了,因为你的装饰器返回的还是一个正常的函数,然后再加上一个@staticmethod是不会出问题的。

class Car(object):

def __init__(self, model):

self.model = model

@staticmethod

@logging # 在@staticmethod之前装饰,OK

def check_model_for(obj):

pass

如何优化你的装饰器:

嵌套的装饰函数不太直观,我们可以使用第三方包类改进这样的情况,让装饰器函数可读性更好。

decorator.py

decorator.py是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数wrapper(),再使用decorate(func, wrapper)方法就可以完成一个装饰器。

from decorator import decorate

def wrapper(func, *args, **kwargs):

"""print log before a function."""

print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

return func(*args, **kwargs)

def logging(func):

return decorate(func, wrapper) # 用wrapper装饰func

你也可以使用它自带的@decorator装饰器来完成你的装饰器。

from decorator import decorator

@decorator

def logging(func, *args, **kwargs):

print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

return func(*args, **kwargs)

decorator.py实现的装饰器能完整保留原函数的name,doc和args,唯一有问题的就是inspect.getsource(func)返回的还是装饰器的源代码,你需要改成inspect.getsource(func.__wrapped__)。

wrapt

wrapt是一个功能非常完善的包,用于实现各种你想到或者你没想到的装饰器。使用wrapt实现的装饰器你不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)也准确无误。

import wrapt

# without argument in decorator

@wrapt.decorator

def logging(wrapped, instance, args, kwargs): # instance is must

print "[DEBUG]: enter {}()".format(wrapped.__name__)

return wrapped(*args, **kwargs)

@logging

def say(something): pass

使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs),注意第二个参数instance是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据instance的值你能够更加灵活的调整你的装饰器。另外,args和kwargs也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。

如果你需要使用wrapt写一个带参数的装饰器,可以这样写。

def logging(level):

@wrapt.decorator

def wrapper(wrapped, instance, args, kwargs):

print "[{}]: enter {}()".format(level, wrapped.__name__)

return wrapped(*args, **kwargs)

return wrapper

@logging(level="INFO")

def do(work): pass

Tensorflow中的@:

tensorflow就巧妙应用的python的装饰器,提高了代码的动态性,也使代码变得精简。

@tf_export 的作用是:Provides ways to export symbols to the TensorFlow API.

@tf_contextlib的作用是:A tf_decorator-aware wrapper for `contextlib.contextmanager`.

还有@tf_inspect、@tf_should_use等。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。


希希
3楼 · 2021-11-16 10:19

TensorFlow™是一个基于数据流编程(dataflow programming)的符号数学系统,被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络算法库DistBelief。

复制代码


20200921文 - 做更棒的自己!
4楼 · 2021-11-25 19:09

TensorFlow是深度学习框架
你需要做深度学习的事,可以选择它,用它自然就导入它
TensorFlow是Python的,所以你见到Python脚本里有这个导入

相关问题推荐

  • 回答 3

    换行。比如,print hello\nworld效果就是helloworld\n就是一个换行符。\是转义的意思,&#39;\n&#39;是换行,&#39;\t&#39;是tab,&#39;\\&#39;是,\ 是在编写程序中句子太长百,人为换行后加上\但print出来是一整行。...

  • 回答 42

    十种常见排序算法一般分为以下几种:(1)非线性时间比较类排序:a. 交换类排序(快速排序、冒泡排序)b. 插入类排序(简单插入排序、希尔排序)c. 选择类排序(简单选择排序、堆排序)d. 归并排序(二路归并排序、多路归并排序)(2)线性时间非比较类排序:...

  • 回答 70
    已采纳

    前景很好,中国正在产业升级,工业机器人和人工智能方面都会是强烈的热点,而且正好是在3~5年以后的时间。难度,肯定高,要求你有创新的思维能力,高数中的微积分、数列等等必须得非常好,软件编程(基础的应用最广泛的语言:C/C++)必须得很好,微电子(数字电...

  • 回答 28

    迭代器与生成器的区别:(1)生成器:生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。而且记录了程序执行的上下文。生成器不仅记住了它的数据状态,生成器还记住了程序...

  • 回答 9

    python中title( )属于python中字符串函数,返回’标题化‘的字符串,就是单词的开头为大写,其余为小写

  • 回答 6

    第一种解释:代码中的cnt是count的简称,一种电脑计算机内部的数学函数的名字,在Excel办公软件中计算参数列表中的数字项的个数;在数据库( sq| server或者access )中可以用来统计符合条件的数据条数。函数COUNT在计数时,将把数值型的数字计算进去;但是...

  • 回答 1

    head是方法,所以需要取小括号,即dataset.head()显示的则是前5行。data[:, :-1]和data[:, -1]。另外,如果想通过位置取数据,请使用iloc,即dataset.iloc[:, :-1]和dataset.iloc[:, -1],前者表示的是取所有行,但不包括最后一列的数据,结果是个DataFrame。...

  • Python入门简单吗2021-09-23 13:21
    回答 45

    挺简单的,其实课程内容没有我们想象的那么难、像我之前同学,完全零基础,培训了半年,直接出来就工作了,人家还在北京大公司上班,一个月15k,实力老厉害了

  • 回答 4

    Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学习到包package时,将会介绍),这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;如果某种操作只对特殊的某一类对象可行,Pyt...

  • 回答 8

     相当于 ... 这里不是注释

  • 回答 4

    还有FIXME

  • 回答 3

    python的两个库:xlrd和xlutils。 xlrd打开excel,但是打开的excel并不能直接写入数据,需要用xlutils主要是复制一份出来,实现后续的写入功能。

  • 回答 8

    单行注释:Python中的单行注释一般是以#开头的,#右边的文字都会被当做解释说明的内容,不会被当做执行的程序。为了保证代码的可读性,一般会在#后面加一两个空格然后在编写解释内容。示例:#  单行注释print(hello world)注释可以放在代码上面也可以放在代...

  • 回答 2

    主要是按行读取,然后就是写出判断逻辑来勘测行是否为注视行,空行,编码行其他的:import linecachefile=open(&#39;3_2.txt&#39;,&#39;r&#39;)linecount=len(file.readlines())linecache.getline(&#39;3_2.txt&#39;,linecount)这样做的过程中发现一个问题,...

  • 回答 4

    或许是里面有没被注释的代码

  • 回答 26

    自学的话要看个人情况,可以先在B站找一下视频看一下

没有解决我的问题,去提问