flask之REDIS+Dplayer消息队列实现电影弹幕

一、安装Redis

官网下载地址:https://redis.io/download

Redis  centos安装过程:

yum -y install gcc gcc-c++
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
tar -zxvf redis-4.0.10.tar.gz
cd redis-4.0.10
make && make install
./utils/install_server.sh
#测试redis是否运行
redis-cli
> ping #返回pong表示成功

安装flask-redis

#清华源
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple flask-redis

二、安装开源插件Dplayer

Dplayer官网地址:http://dplayer.js.org/#/    (有时候会打不开哦)

Dplayer github地址:https://github.com/MoePlayer/DPlayer

#html引入css/js
<link rel="stylesheet" href="{{ url_for('static',filename='dplayer/dist/Dplayer.min.css') }}" >
<script src="{{ url_for('static',filename='dplayer/dist/DPlayer.min.js') }}"></script>
#引用到页面
<div id="dplayer" style="height: 500px;width: 774px"></div>
#弹幕和视频接口
 <script>
        var dp1 = new DPlayer({
            container: document.getElementById('dplayer'),
            video: {
                url: "{{ url_for('static',filename='uploads/'+movie.url) }}"
            },
            danmaku: {
                id: '{{ movie.id }}',
                api: "/tm/"
            }
        });
 </script>
 
#Python返回弹幕的处理方法
@home.route("/tm/v3/",methods=["GET","POST"])
def tm():
    import json
    if request.method=="GET":
        #获取弹幕消息队列
        mid = request.args.get("id")
        key="movie"+str(mid)
        if rd.llen(key):
            msgs=rd.lrange(key,0,2999)
            res={
                "code":0,
                "data":[json.loads(v) for v in msgs]
            }
        else:
            res={
                "code":1,
                "danmaku":[]
            }
        resp=json.dumps(res)
    if request.method=="POST":
        #添加弹幕
        data=json.loads(request.get_data())
        msg= {
        "__v": 0,
        "_id": datetime.datetime.now().strftime("%Y%m%d%H%M%S") + uuid.uuid4().hex,
        "author": data["author"],
        "time": data["time"],
        "text": data["text"],
        "color": data["color"],
        "type": data["type"],
        "ip": request.remote_addr,
        "player": data["id"]
        }
        res = {
            "code": 0,
            "danmaku":msg
        }
        resp=json.dumps(res)
        msg=[data["time"],data["type"],data["color"],data["author"],data["text"]]
        rd.lpush("movie"+str(data["id"]),json.dumps(msg))
    return Response(resp,mimetype="application/json")

python日志:logging模块使用

何时使用logging

想要执行的任务工具
对于命令行或程序的应用,结果显示在控制台。print()
在对程序的普通操作发生时提交事件报告 (比如:状态监控和错误调查)http://logging.info() 函数 (当有诊断目的需要详细输出信息时使用
logging.debug() 函数)
提出一个警告信息基于一个特殊的运行时事件warnings.warn() 位于代码库中,该事件是可以避免的,需要修改
客户端应用以消除告警logging.warning() 不需要修改客户端应用,但是该事件还是需要引起关注
报告错误而不引发异常 (如在长时间运行中的服务端进程的错误处理)logging.error(),
logging.exception() 或 logging.critical() 分别适用于特定的错误及应用领域

日志事件级别

级别何时使用
DEBUG细节信息,仅当诊断问题时适用。
INFO确认程序按预期运行
WARNING表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行
ERROR由于严重的问题,程序的某些功能已经不能正常执行
CRITICAL严重的错误,表明程序已不能继续执行

默认的级别是 “WARNING“,意味着只会追踪该级别及以上的事件,除非更改日志配置。所追踪事件可以以不同形式处理。最简单的方式是输出到控制台。另一种常用的方式是写入磁盘文件。


basicConfig()设置

# -*- coding:utf-8 -*-

import logging

#默认的warning级别,只输出warning以上的
#使用basicConfig()来指定日志级别和相关信息

logging.basicConfig(level=logging.DEBUG #设置日志输出格式
                    ,filename="demo.log" #log日志输出的文件位置和文件名
                    ,filemode="w" #文件的写入格式,w为重新写入文件,默认是追加
                    ,format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" #日志输出的格式
                    # -8表示占位符,让输出左对齐,输出长度都为8位
                    ,datefmt="%Y-%m-%d %H:%M:%S" #时间输出的格式
                    )

logging.debug("This is  DEBUG !!")
logging.info("This is  INFO !!")
logging.warning("This is  WARNING !!")
logging.error("This is  ERROR !!")
logging.critical("This is  CRITICAL !!")

#在实际项目中,捕获异常的时候,如果使用logging.error(e),只提示指定的logging信息,不会出现
#为什么会错的信息,所以要使用logging.exception(e)去记录。

try:
    3/0
except Exception as e:
    # logging.error(e)
    logging.exception(e)

输出日志如下:

2021-03-26 02:03:01 - root - DEBUG     - basiclogging : 16 line - This is  DEBUG !!
2021-03-26 02:03:01 - root - INFO      - basiclogging : 17 line - This is  INFO !!
2021-03-26 02:03:01 - root - WARNING   - basiclogging : 18 line - This is  WARNING !!
2021-03-26 02:03:01 - root - ERROR     - basiclogging : 19 line - This is  ERROR !!
2021-03-26 02:03:01 - root - CRITICAL  - basiclogging : 20 line - This is  CRITICAL !!

2021-03-26 03:16:47 - root - ERROR     - basiclogging : 27 line - division by zero
Traceback (most recent call last):
  File "E:/pycharm_project/examples-of-web-crawlers-master/logginng/basiclogging", line 24, in <module>
    3/0
ZeroDivisionError: division by zero


logging的高级应用

logging采用了模块化设计,主要包含四种组件:

Loggers:记录器,提供应用程序代码能直接使用的接口。(类似于笔,使用不同的笔记录不同的日志,例如一个项目里有很多功能模块,每个模块可以设置不同的Logger去记录,然后设置每只笔的不同格式)

Handlers:处理器,将记录器产生的日志发送到目的地。(输出到文件或者控制台或者邮件等,一个记录器可以对应多个处理器。)

Filters:过滤器,提供更好的粒度控制,决定哪些日志会被输出。

Formatters:格式化器,设置日志内容的组成结构和消息字段。

Loggers记录器

  1. 提供应用程序的调用接口
logger = logging.getLogger(__name__)
#logger是单例的,也就是__name__不变,获取到的logger都是同一个

2.决定日志记录的级别

logger.setLevel()

3.将日志内容传递到相关联的handlers中

logger.addHandler()
logger.removeHandler()

Handler处理器

将日志发送到不同的地方

#常见的处理器
StreamHandler #屏幕输出
FileHandler #文件记录
BaseRotatingHandler #标准的分割文件日志
RotatingFileHandler #按文件大小记录日志
TimeRotatingFileHandler #按时间记录日志

StreamHandler

标准输出分发器,也就是屏幕显示

sh = logging.StreamHandler(stream=None)

FileHandler

将日志保存到文件中

fh = logging.FileHandler(filename,mode='a',encoding=None,delay=False)

setFormatter()

设置当前Handler对象使用的消息格式

Formatters格式

formatter对象用来最终设置日志信息的顺序,结构和内容

ft = logging.Formatter.__init__(fmt=None,datafmt=None)

logging编程方式代码demo

# -*- coding:utf-8 -*-

import logging

#编程的方式记录日志

#记录器
logger1 = logging.getLogger("logger1")
logger1.setLevel(logging.DEBUG)
print(logger1)
print(type(logger1))

logger2 = logging.getLogger("logger2")
logger2.setLevel(logging.INFO)
print(logger2)
print(type(logger2))


#处理器
#1.标准输出
sh1 = logging.StreamHandler()

sh1.setLevel(logging.WARNING)

sh2 = logging.StreamHandler()


# 2.文件输出
# 没有设置输出级别,将用logger1的输出级别(并且输出级别在设置的时候级别不能比Logger的低!!!),设置了就使用自己的输出级别
fh1 = logging.FileHandler(filename="fh.log",mode='w')

fh2 = logging.FileHandler(filename="fh.log",mode='a')
fh2.setLevel(logging.WARNING)

# 格式器
fmt1 = logging.Formatter(fmt="%(asctime)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s")

fmt2 = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s"
                        ,datefmt="%Y/%m/%d %H:%M:%S")

#给处理器设置格式
sh1.setFormatter(fmt1)
fh1.setFormatter(fmt2)
sh2.setFormatter(fmt2)
fh2.setFormatter(fmt1)

#记录器设置处理器
logger1.addHandler(sh1)
logger1.addHandler(fh1)
logger2.addHandler(sh2)
logger2.addHandler(fh2)

#打印日志代码
logger1.debug("This is  DEBUG of logger1 !!")
logger1.info("This is  INFO of logger1 !!")
logger1.warning("This is  WARNING of logger1 !!")
logger1.error("This is  ERROR of logger1 !!")
logger1.critical("This is  CRITICAL of logger1 !!")

logger2.debug("This is  DEBUG of logger2 !!")
logger2.info("This is  INFO of logger2 !!")
logger2.warning("This is  WARNING of logger2 !!")
logger2.error("This is  ERROR of logger2 !!")
logger2.critical("This is  CRITICAL of logger2 !!")

控制台输出:

2021-03-26 02:38:56,370 - WARNING   - advancelogging : 56 line - This is  WARNING of logger1 !!
2021-03-26 02:38:56,370 - ERROR     - advancelogging : 57 line - This is  ERROR of logger1 !!
2021-03-26 02:38:56,370 - CRITICAL  - advancelogging : 58 line - This is  CRITICAL of logger1 !!
2021/03/26 02:38:56 - logger2 - INFO      - advancelogging : 61 line - This is  INFO of logger2 !!
2021/03/26 02:38:56 - logger2 - WARNING   - advancelogging : 62 line - This is  WARNING of logger2 !!
2021/03/26 02:38:56 - logger2 - ERROR     - advancelogging : 63 line - This is  ERROR of logger2 !!
2021/03/26 02:38:56 - logger2 - CRITICAL  - advancelogging : 64 line - This is  CRITICAL of logger2 !!
<Logger logger1 (DEBUG)>
<class 'logging.Logger'>
<Logger logger2 (INFO)>
<class 'logging.Logger'>

文件fh.log输出:

2021/03/26 02:38:56 - logger1 - DEBUG     - advancelogging : 54 line - This is  DEBUG of logger1 !!
2021/03/26 02:38:56 - logger1 - INFO      - advancelogging : 55 line - This is  INFO of logger1 !!
2021/03/26 02:38:56 - logger1 - WARNING   - advancelogging : 56 line - This is  WARNING of logger1 !!
2021/03/26 02:38:56 - logger1 - ERROR     - advancelogging : 57 line - This is  ERROR of logger1 !!
2021/03/26 02:38:56 - logger1 - CRITICAL  - advancelogging : 58 line - This is  CRITICAL of logger1 !!
2021-03-26 02:38:56,370 - WARNING   - advancelogging : 62 line - This is  WARNING of logger2 !!
2021-03-26 02:38:56,370 - ERROR     - advancelogging : 63 line - This is  ERROR of logger2 !!
2021-03-26 02:38:56,370 - CRITICAL  - advancelogging : 64 line - This is  CRITICAL of logger2 !!

过滤器在代码里的应用

#定义一个过滤器
flt1 = logging.Filter("logger1") #名字就是logger记录器的名字

#关联过滤器
#1.logger关联过滤器
logger.addFilter(flt1)

#2.处理器关联过滤器
sh1.addFilter(flt1)
#fh1.addFilter(flt1)

#过滤器定义在记录器上的时候,不管你设置了任何关于这个过滤器的任何设置,都不起作用。
#过滤器定义在哪个处理器上,哪个处理器就不起作用,也就可以控制日志的输出,想要还是不想要。
#特别是在项目调试的时候,不想让日志记录到文件里去,直接在屏幕显示,就过滤到fh
#代码上线后,日志记录到文件,不用在屏幕显示,就过滤掉sh

文件配置logging编程

上述的方法都写的很死板,在小工程或者单个文件还可以,但是当项目大了,我们就必须使用配置文件的方式配置logging的信息,从而来灵活使用,主要有两种方式去配置,一种是conf文件,一种是字典文件。

import logging.config
logging.config.fileConfig("/path/to/configfile")

logging.config.dictConfig("/path/to/configfile")

yaml配置文件:

version: 1
#是否覆盖掉已经存在的loggers
disable_existing_loggers: True

formatters:

  tostrout:
    format: "%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s"
    datefmt: "%Y/%m/%d %H:%M:%S"

  tofile:
    format: "%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s"

handlers:
  sh:
    class: logging.StreamHandler
    level: WARNING
    formatter: tostrout
    stream: ext://sys.stdout

  fh:
    class: logging.handlers.TimedRotatingFileHandler
    filename: logconfdd.log
    interval: 1
    backupCount: 2
    when: D
    level: INFO
    formatter: tofile

loggers:
  logger1:
    level: DEBUG
    handlers: [sh]
    #是否往上级Logger传递,如果为yes的话,root选择了两个logger,这里的日志也会在两个logger的配置中输出,会重复。所以选No,自己记录自己的日志。
    propagate: no

  logger2:
    level: INFO
    handlers: [fh]
    propagate: no

root:
  level: DEBUG
  handlers: [sh,fh]
  propagate: no

调用代码:

# -*- coding:utf-8 -*-

import logging
import logging.config
import yaml

with open("logconf.yml", "r") as f:
    dict_conf = yaml.safe_load(f)

logging.config.dictConfig(dict_conf)
root = logging.getLogger()
logger1 = logging.getLogger('logger1')
logger2 = logging.getLogger('logger2')

#打印日志代码

root.debug("This is  DEBUG of root !!")
root.info("This is  INFO of root !!")
root.warning("This is  WARNING of root !!")
root.error("This is  ERROR of root !!")
root.critical("This is  CRITICAL of root !!")

logger1.debug("This is  DEBUG of logger1 !!")
logger1.info("This is  INFO of logger1 !!")
logger1.warning("This is  WARNING of logger1 !!")
logger1.error("This is  ERROR of logger1 !!")
logger1.critical("This is  CRITICAL of logger1 !!")

logger2.debug("This is  DEBUG of logger2 !!")
logger2.info("This is  INFO of logger2 !!")
logger2.warning("This is  WARNING of logger2 !!")
logger2.error("This is  ERROR of logger2 !!")
logger2.critical("This is  CRITICAL of logger2 !!")

标准输出:

021/03/27 12:18:58 - root - WARNING   - loggingofconfing : 19 line - This is  WARNING of root !!
2021/03/27 12:18:58 - root - ERROR     - loggingofconfing : 20 line - This is  ERROR of root !!
2021/03/27 12:18:58 - root - CRITICAL  - loggingofconfing : 21 line - This is  CRITICAL of root !!
2021/03/27 12:18:58 - logger1 - WARNING   - loggingofconfing : 25 line - This is  WARNING of logger1 !!
2021/03/27 12:18:58 - logger1 - ERROR     - loggingofconfing : 26 line - This is  ERROR of logger1 !!
2021/03/27 12:18:58 - logger1 - CRITICAL  - loggingofconfing : 27 line - This is  CRITICAL of logger1 !!

文件输出(logconfdd.log):

2021-03-27 12:18:58,301 - root - INFO      - loggingofconfing : 18 line - This is  INFO of root !!
2021-03-27 12:18:58,301 - root - WARNING   - loggingofconfing : 19 line - This is  WARNING of root !!
2021-03-27 12:18:58,301 - root - ERROR     - loggingofconfing : 20 line - This is  ERROR of root !!
2021-03-27 12:18:58,301 - root - CRITICAL  - loggingofconfing : 21 line - This is  CRITICAL of root !!
2021-03-27 12:18:58,301 - logger2 - INFO      - loggingofconfing : 30 line - This is  INFO of logger2 !!
2021-03-27 12:18:58,301 - logger2 - WARNING   - loggingofconfing : 31 line - This is  WARNING of logger2 !!
2021-03-27 12:18:58,301 - logger2 - ERROR     - loggingofconfing : 32 line - This is  ERROR of logger2 !!
2021-03-27 12:18:58,301 - logger2 - CRITICAL  - loggingofconfing : 33 line - This is  CRITICAL of logger2 !!

本文转自知呼

音乐播放器API

为WordPress网站添加一个音乐播放器,网上大部分的教程都是使用各种插件,有的插件还需要花钱买授权,用着不爽。本文用纯代码为网站添加音乐播放器,安全可靠,可以任意修改。

1、Aplayer

其实很简单,Github有很多大神开源了相关的内容。用着最舒服的,当属Aplayer了,可以去官网体验一下:

​ ​https://aplayer.js.org/​​

文档手册也很详细:

​ ​https://github.com/MoePlayer/APlayer​​

方法很简单,加载Aplayer的js和css,在想要展先的位置调用即可。Aplayer支持很多模式,上图展示的效果为吸底模式,就是在网页的页面最左下角展示。本文以吸底模式为例进行说明,如果想采用其它模式,可以根据文档手册修改代码。

2、Meting

如果只使用Aplayer,那么需要指定音乐的图片地址,mp4地址,歌词等,用着不是很方便,这时就可以使用Meting作为辅助:

​ ​https://github.com/metowolf/MetingJS​​

Meting为Aplayer提供了网易云音乐的API接口,只要获得网易云音乐歌单的ID,就可以自动加载歌单里所有的歌曲,直接调用,方便很多,当然除了网易云音乐的API,还有其他的接口。

3、开工

如果上述这些说了都不懂,没关系,跟着接下来的步骤走即可。

首先下载为各位打包好的js和css文件(下载地址)

解压后,会看到三个文件APlayer.min.css、APlayer.min.js、Meting.min.js,将两个js文件放到主题的js文件夹中,将css文件放到主题的css文件夹中。

在主题目录下的footer.php中,添加如下代码:


将代码中的wp-content/themes/construction-base改为你的WordPress主题的根目录。保存好后,刷新网页就可以看到播放器了。

1. <link rel="stylesheet" href="wp-content/themes/construction-
2. base/css/APlayer.min.css"><script src="wp-content/themes/construction-base/js/APlayer.min.js">
3. </script>
4. <div class="aplayer"
5. data-id="969649908"
6. data-fixed="true" data-
7. server="netease"
8. data-volume="0.8"
9. data-type="playlist">
10. </div>
11. <script src="wp-content/themes/construction-base/js/Meting.min.js">
12. </script>

但是这个播放器的歌单是我指定的歌单,如果想用自己的网易云音乐歌单,可以修改data-id的值,这个值的获取方法也很简单:

打开浏览器访问网页版的网易云音乐,地址栏的id参数就是对应的data-id值,自己创建一个歌单,想放什么音乐就放什么音乐。

PS:歌单更新歌曲实时更新

小农思想

传统文化里,有两个东西,很操蛋,碰什么,什么完。

第一个是官僚主义,碰电影,电影卒;碰足球,足球亡;碰动漫,又把本来风生水起的国漫,搞得奄奄一息。

第二个就是小农思想,沾上你,就势必要把你异化成牛马,让你平庸且痛苦。你抗拒?那就把你打成异端,极尽批评、排斥和孤立。

巧的是,小农思想恰好是官僚主义在民间的投影,它们相伴相生。

秦始皇和汉武帝都想打造出一套高效的中央集权体制,以便集中全天下的资源为自己所用,只是他们用到的办法,大不相同。

秦始皇用的是暴力,结果二世而亡。

汉武帝吸取了这个教训,采用了更温和的‘教化’手段,用哲学家福柯的话说,就是从‘刑罚社会’,过渡到了‘规训社会’。

从此,中央集权制登上历史舞台,并绵延了两千多年。

虽然汉武帝换了条路线,但他及后世皇帝的统治目标,和秦始皇一毛一样,所谓‘外儒内法’,‘百代以来,皆行秦法’说的就是这个意思。

秦法是什么法?《商君书》说——贫民、疲民、辱民、愚民、弱民。

即秦法要打造的,是只知低头干活,毫无反抗意志的工具人,

假设我是官,你是民,你觉得我会怎么控制你?

首先是使你保持穷困,人穷则志短,这样随便给你点甜头,就能役使你。

其次就是用垄断刚需产品的方式抬高生活成本,让你为获取生存物资而不停劳作,这样你就不会想七想八,从而利于控制。

第三是就是给社会分三六九等,并在文化上羞辱底层,打击你的自尊自信,使你自轻自贱,觉得穷人就不该如何如何,就会迷信权威,认为地位越高的人,说话就越对。

第四是干掉那些刺头,剩下的都是软蛋。

最后就是搞知识垄断,使你的认知维持在低水平,这样你就无法识破社会的运转逻辑,从而更容易被控制。

但具体怎么实施呢?

无非就是历史书上常说的重税,连坐,重农抑商,利出一孔……但这些政策的实施,最终都落在“小农经济”这个环节。

要提取经济资源,同时要贫民、疲民,就得重税,但你是不是觉得史书记载的农业税并不高,谈不上重税?

对土地征的税,可能是不高,但是还有另外两种税,一是徭役,杜甫《石壕吏》中‘急应河阳役,犹得备晨炊’说的就是这个。

二是“盐铁官营”,相当于古代版的工农业剪刀差,都是税。

为了更大限度地从民间提取劳动力,历朝历代的统治者都最喜欢农民,因为农民是被绑在一亩三分地上的,控制成本最低,人为阻止人口流动的保甲制、里坊制、连坐制……都是证据。

古代皇帝动不动就说重农抑商,其实他们讨厌的是商人,而不是商业,他们要抑制的也只是民商,因为要把高利润的商业机会,全都垄断到官商手中,“盐铁官营”就是很重要的一例。

皇帝认为商人有很强的组织能力,会威胁君主权威,是必须要干掉的强民。

除了商人,还有豪侠,这些人总爱打抱不平,所以一呼百应,也是威胁。另外还有知识分子,他们来自诸子百家,思想自由,不光难以被规训,还会煽动其他人。

总之就是要“焚百家之言,以愚黔首;隳名城,杀豪杰……以弱天下之民”。

现在想想,乾隆拒绝与英国通商,真的是他傲慢无知?恐怕是他太了解商业所塑造的资产阶级会撼动皇权了吧。

不让做商人,也不让做豪侠,还不让多读书胡思乱想……

什么叫“利出一孔”,就是锁死一切人们可以自我发展的上升通道——想要升官发财,只能依附于它。

利出一孔+依附体制,对于普通老百姓来说,结果就是出路单一,于是内卷文化,由此而生!

也就是说,统治者自己不想当农民,但希望别人都是农民——不要乱跑,不要乱想,归顺他,依附他……总之,农耕文明并不是自然形成的,而是一种体制的筛选

脆弱,就会畏惧不稳定。

畏惧不稳定,就会进化出求稳的思想,然后便逐渐演化出后来我们所看到的小农社会的特点,比如宗族依附、思想保守……

不论是古代还是现代,要实现阶级爬升,最首要的,就是使自己不处于被剥削的位置。

小农经济本身就是阶级制度下的创造,小农思想也是规训的思想、奴隶的思想,当一个人的大脑全按小农的思维方式运作,他怎么可能会免于被剥削呢?

我们说,一个人的行为和情绪,是受思想控制的,那思想又来源于哪里?具体地说,它受到3种力量的塑造:

1)“自私的基因”;

2)教化和规训;

3)自由意志。

比如男人钟爱大胸美女,是“自私的基因”控制的;钟爱白幼瘦,是网红文化影响的;

而阿强无论说什么就是钟爱阿珍,则是自由意志的结果。

再代入一个想象,假设你建了一台机器人,专门用来处理垃圾,那么倒垃圾,就是它的初级设定。

后来你又给它写了一段算法,使它掌握了机器学习的能力,还给它灌入一大堆数据,让它边分类垃圾边学习。

渐渐的,你一个眼神,一声叹息,它都可以智能地“体会”到你的意思,并精准按你的要求,提供服务。

再后来,他有了自己的意志,产生了他自己的需求和目标,他的行为,不再局限于倒垃圾,也不再围绕“服务你”而展开,他有时去晒太阳,有时去周游世界……那这时我们就会说——他是一个人,不再是一台机器。

所以智慧是什么?

智慧就是使自由意志彻底覆盖“自私的基因”和文化传统对人的控制,孔子说的“从心所欲不逾矩”和王阳明说“知行合一”,都是这个意思。

综上:

1)皇帝要搞中央集权,其终极目标,就是最大限度地控制社会,并提取经济资源,为自己所用;

2)小农经济,就是为满足集权目标,而筛选出来的“控制型”经济模式;

3)小农思想,是产生于小农经济中的规训的思想、奴隶的思想;

4)小农思想是创新和科学的敌人;

5)小农思想是规训的结果,非常隐蔽,绵延两千多年,以内化到我们的文化基因中;

6)要想离开被剥削的位置,最起码的,你得剔除出思维习惯中的小农意识;

7)所谓智慧,就是使自由意志,压过基因算法和规训社会对人的控制。

这么多失业年轻人,该怎么办

有治本和治标两种办法。

治本,就是要改革经济分配的结构。因为现在所有的经济问题,都是r>g(资产收益率 > 劳动收益率)造成的。

所谓改革,就是要降低资产收益率,提高劳动收益率,最起码,要提高真实利率到正的,以减少金融对财富分配的干涉。

但很难,就跟“改漕为海”一样难。

并非难在制度设计,而是难在阻力太大。

由于社会并不朝着最正确的方向发展,而是朝着阻力最小的方向前进,所以还是治标更容易些。

治标的办法很多人都能想到,

第一招就是基建,罗斯福新政以及日本“失去的三十年”中,都曾被反复使用,我们过去在治理衰退时,也一直没停。

但实话讲,基建对于解决年轻人的就业,效果有限。

一是因为它的直接受益者,是地方的“裙带资本”+部分农民工,最多就是给部分土木专业的毕业生提供少量机会,但更多学生不是这个专业的。

二是基建项目是一次性的,建完就撤,而且大多亏损运营,所以它带来的岗位并不能“持续”。

我知道有人会觉说基建的功能主要不是提供就业,而是“直升机撒钱”,钱撒下去,驱动消费,就可以间接激活就业。

但问题就出在这个“消费”上——假如你现在得到一笔钱,你会拿去消费掉么?你不会。

如果你是“裙带资本”,那你的需求就不是消费,而是投资,你往四周一看——就没什么靠谱项目嘛,于是你选择把钱存进银行。

如果你是农民工,你想到现在收入很不稳定,再加上还要帮孩子存钱买房,于是你也选择把钱存进银行。

印钞大法是好,但如果印出来的钱,没有进入“生产⇆消费”循环的话,就没卵用。

第二招是院校扩招+延长学制。

也就是说,研究生扩招的目的,并不是让你去学东西的,而是要让你内卷在“考”上,茫然在“学”上,总之不要泛滥在社会上。

还有第三招,就是“回农村”。

温铁军一直说农村是经济危机的安全垫,不光因为它可以对失业青年进行消化吸收,还因为农村相对“稳定”。

直白的说,就是失业青年放在城市容易出乱子,但放到农村,就会安分不少。

形象的理解Class的init、self 、@staticmethod、 @classmethod、@property

@staticmethod、@classmethod、@property这几个带@符号的语法是Python里的「语法糖」。这些语法中文常译作「装饰器」,对于没有任何编程经验的新手,这些都是经常难以理解的地方。再有就是类的self参数,新手通常留于表面的认识造成一些误会。

需要说明一下,本教程针对Python3。

要理解这几样,我们先要对Python类有一个认识,让我们先简单回顾一下「类」。

class 类

我们学习编程语言时,了解到语言有面向过程和面向对象之分,可以面向对象的语言一般都是「高级语言」,为什么高级?因为使用更容易,理解更容易,当然这一切都有代价,但那是另一件事情了,我们有机会在说。

「在Python中一切皆对象」这句话估计你也不是头一回听到了。Python中对象最直接的化身便是class语法,中文称之为「类」,让我们基于Python造「一批」Girlfriend(对象)吧。注意:这里我强调了「一批」这个词。

    # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

    # 生产几个女朋友
    lili = Girlfriend()
    mimi = Girlfriend()

以上,我们做了这几件事情:

1)我们定义了一个Girlfriend类,用来一会生产女朋友当相好的对象;

2)基于我低俗的品味,女朋友要定义几个属性:年纪要22岁,罩杯要C,为了谨防意外,性别定义清楚是女,免得造出人妖;

3)基于我的承受能力,我实例化了lili和mimi两个女朋友,两个就够了。

通过上述例子,我们回顾了类与实例的关系:类就好比生产具体产品的「模具」或者「模子」,先做好模具,定义好将来生产的产品期望的样子,然后再用实例化语法通过模具生产一些产品。

我可以随时通过「实例 . 属性」这个语法知道女朋友的一些属性值,比如:

    lili.age  # 会得到22这个值
    mimi.age  # 也是22
    lili.size  # C杯

显然,我的两个女朋友年纪、罩杯都是一致的,因为我在定义模具(类)的时候就明确指定了这些属性。

class的init方法和self参数

什么都一样没意思,我希望至少每个妞的发型不要一样,并且我能指定,想要什么发型就造出来什么发型的女朋友。注意,我的需求有一个细节:女朋友的头发是在女朋友造出来后就已经有的。要想实现这个需求细节,意味着必须在生产女朋友的过程中就完成发型的构建。如果你还有印象,应该依稀还记得Python有一个内置的配合class用的方法init。

init的作用很容易理解,就是每次实例化一个类的时候,会默认自动调用执行这个方法,完成这个方法里定义的行为。这正好符合我们上面的需求,即在生产过程中,加一个发型工艺环节。现在我们改造一下模具和生产工艺:

    # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'
        def __init__(self, hair):
            self.hair = hair
    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')

我们来检验一下结果:

    lili.age  # 会得到22这个值
    mimi.age  # 也是22
    lili.hair  # 结果是'乌黑长发'
    mimi.hair  # 结果是'金黄短发'

非常好,两个妞发型已经不一样了。

我们已然知道init的作用:在实例化一个类的时候,会默认自动调用这个方法执行。这就是说,我们不必明显的告诉Python去执行这个方法,在运行下面这行代码时,Python已经悄悄自己执行这个方法了:

 lili = Girlfriend('乌黑长发')

需要明显的告诉程序去干什么事情,有一个针对的术语叫「显式」;相对应的,不需要明显的告诉程序去干事情,程序会按照既定的规则自己办好事情叫「隐式」。在以后的文章中,我们将会再看见这些词,请大家理解和记住意思。

显然,init方法被隐式调用了。我们只在定义class时定义了这个方法,但后续并没有显式的调用他,但他确实执行了,帮我的女朋友做好了发型。如前所述,隐式调用是程序按照「既定」的规则办事,既定的意思就是事先都定下来了怎么做,既然如此,就不能随意变更事先定下的细节。因此,init方法的在名称上很特别,init前后各有两根下划线,这种加下划线的方法名你现在只需记得其目的就是不要让你随便改动。所以,init的方法名不能有任何改动,少根下划线都不行。如果改动,则Python不会把改动后的方法当成init方法去隐式调用了。

让我们再来看看self这个参数。当我们在定义类时,显然无法确定后续会造多少个女朋友,也无法确定女朋友的名字,更无法确定她们的发型。所以我们不能再用定义年纪、性别、罩杯类似办法去定义发型,否则只会导致发型千篇一律。基于此,我们用了init方法,在生产过程中完成发型工艺,并在init方法里定义接收两个参数:self和hair,当我们生产女朋友时:

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')

对于init工艺,其实我们是这样告诉程序的:

lili和mimi年纪、性别、罩杯都一样

lili的hair是’乌黑长发’

mimi的hair是’金黄短发’

对于「lili的hair是’乌黑长发’」这句话,本质上是在说明「谁的头发是什么发型?」。所以有两层意思:1)谁的头发?2)头发的发型是什么?,两层意思组合在一起才是有意义的,完整的意思表达,缺一不可。我们套上程序来看,显然程序刚好也要求传入两个参数。参数hair对应「头发的发型是什么?」再显然不过,那么剩下的self应该就是对应「谁的?」了。但是「谁的」在英文里不是「whose」吗?所以这样行不行:

    # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(whose, hair):
            whose.hair = hair

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')

即self参数更名为whose,行不行?行!

举上述的这个例子,是要明确的告诉你self这个参数名并不是Python的关键字,没有不能更名的限制,然而由于全世界程序员的习惯,约定俗成总是用self这个英文单词,所以你在Python代码中经常看见他。而在Python中,「谁的?」这个信息通常就是实例本身。实例就是造出来的女朋友,女朋友是「对象」,所以实例也是对象。既然self是用来接收实例,那么self最后也是对象,综上,形成了你以前也许已经看到或了解的几个概念:

1)self的作用是用来接收实例的,用来形成实例的命名空间(这涉及Python里「命名空间」的概念,你要有一个初步的认识,否则这句话看不懂。命名空间的概念以后会讲到)。

2)self类似于中文里的「你、我、他、她、它」的概念,在中文中「你、我、他、她、它」指代了具体的人或物,在Python中self则指代了具体的实例,实例就是「lili = Girlfriend(‘乌黑长发’)」这段代码等于号左边的那个具体的东西。指代在中文里也可以换种说法:「引用」。

3)既然self其实就是指代了具体的实例,那么self也将是对象。

现在,你应该对class的init和self的应用场景以及含义有了进一步的认识。

@staticmethod

让我们接着再对模具做一个微小的改进:

  # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(self, hair):
            self.hair = hair

        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')

上面微小的改进中,我们增加了一个名为「about_me()」方法,顾名思义,通过这个方法两位美女能来一小段自我介绍,我们尝试一下:

    lili.about_me()  # 结果:年芳22,性别女,身材C棒棒哒,一头乌黑长发
    mimi.about_me()  # 结果:年芳22,性别女,身材C棒棒哒,一头金黄短发

真好,我们不用再逐个查询美女的属性了。通过上面的这个例子,美女会通过自我介绍直接告诉我她的全部信息。format方法是一个在Python3中出现的基础方法,用来格式化字符串,任何Python3的基本教程里都会谈及这个方法,这里不再赘述。

在你用PyCharm或其他比较智能的编辑器写about_me这个方法时,编辑器会自动往括号里写一个self参数。我十分好奇去掉会怎么样,那就去掉试试:

    # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(self, hair):
            self.hair = hair

        def about_me():
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(age, sex, size, hair))

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')

我们最新的模具里,将about_me方法中原来的参数self去掉了。而运行生产过程时,居然没有任何报错,可以运行。使用类似lili.age的属性查询也正常,接着我们试试lili.about_me(),程序报错:

Traceback (most recent call last):

File “<input>”, line 1, in <module>

TypeError: about_me() takes 0 positional arguments but 1 was given

报错信息很好理解,意思是about_me()方法不接受任何参数,但我们试图要传入一个。这就奇怪了,我们刚才还有之前运行about_me() 方法时,都没有传入任何参数。确实,我们的确没有在about_me() 的括号里显式的传入任何参数,但前面我们已经知道程序会隐式的干一些事情。没错,答案就是每次运行一个实例方法时,程序会自动将实例作为第一个参数传入到方法,这是程序在「硬肛」方法,而显然我们把方法原来的唯一的「入口」self参数给去掉了,没了「入口」,程序「硬肛」折了,所以报错埋怨我们了。再来看about_me方法里,我们在format里亦传入了age, sex, size, hair这四个变量。format方法是在about_me里运行的,基于我们已经了解的基本语法原则,显然这有问题。因为方法里运行的没有定义赋值的变量,都要靠方法本身传入的参数去一一对应,这便是方法参数存在的意义。所以我们恢复原样,先对about_me方法要求传入一个参数用来接应实例,先解决程序要「硬肛」的需求。文章之前讲self时,我们已经知道接收实例的参数约定俗称用self这个单词,所以我们也用这个。那又冒出一个问题:既然format要用到四个参数,为什么我们只传入了一个?答案也许你有了。没错,self传入的是一整个实例对象,或者说是一整个美女,所以美女的年龄,美女的性别,美女的头发都可以知晓了,只需要用我们熟悉的「美女.属性」就可以得到,而结合之前谈到的指代关系,那么顺理成章就是用「self.age」这样的方式就能获取到所需的属性了,因而我们只需要self一个参数即可。

对恢复原样后的模具,我们再加以改进,一样,「做一点微小的工作」:

# 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(self, hair):
            self.hair = hair

        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

        def bio():
            print('全心全意为主人服务!')

为了节省一点篇幅,我就不重复「生产几个女朋友」的过程了。让我们看上面的这个模具,我们又增加了一个名为bio的方法,顾名思义,就是让造出来的美女口头禅就是这个。有了前面的经验,我们可以猜到bio这个方法肯定会出问题。因为程序还会没节操的「硬肛」,所以肯定会报错。但bio这个方法本身就是打印一句口头禅,不需要任何参数参与,总不能脱了裤子放屁,为了能正常运行在不需要的情况下也开个入口(设置一个参数)吧?

非常好的是,Python早已贴心的为我们应对这种场景提供了手段,是时候请出本节的主角@staticmethod了,让我们稍加改进一下方法:

# 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(self, hair):
            self.hair = hair

        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

        @staticmethod    
        def bio():
            print('全心全意为主人服务!')

真是微小的工作,我们在bio方法上面一行增加了@staticmethod。先别管太多,让我们运行一下lili.bio()看看:

 lili.bio()  # 结果:全心全意为主人服务!

结果正常!现在再抱着钻研的心态,对bio方法增加一个self参数,我们再运行一把看看。结果报错:

Traceback (most recent call last):

File “<input>”, line 1, in <module>

TypeError: bio() missing 1 required positional argument: ‘self’

意思也很好理解,这回程序埋怨的是「我不想肛了你非要弄个入口让我肛」,即方法要求传入一个参数,但程序没有任何参数传入。

所以@staticmethod的应用场景和作用也显而易见了。一旦对一个方法应用@staticmethod,则在实例调用这个方法时,不会再试图传入实例本身。这样很容易联想到@staticmethod就是用在那些不需要实例直接参与的方法上。那什么样的类方法不需要实例直接参与呢?最常见的莫过于两种:一种是我们上面举的例子,即实例通用的方法。另一种是供实例方法调用的方法,比如类里有方法一,方法二,方法三,方法二和方法三需要借助方法一运算出一个结果,这时会在方法二、方法三的代码里调用方法一,方法一的作用只在接收两个参数然后返回一个运算结果,不需要传入实例,这时可以在方法一上写@staticmethod,而且这种例子你可以猜到方法一应该是算一个(仅供内部调用的)特殊方法,并不希望被外部随意调用,所以这种方法的函数名通常会在前面加一根下划线。

在谈到@staticmethod时举的例子其实相当无聊,上面说的方法一、二、三的例子其场景是很少发生的,所以@staticmethod的出镜率相对而言其实很低,很少被用到。

@classmethod

当我们理解@staticmethod的场景和作用后,理解@classmethod也将变得容易得多。因为某种程度上说,@classmethod和@staticmethod有共通的地方。还是让我们还是从改造模具开始:

 # 定义类
    class Girlfriend:
        age = 22
        sex = '女'
        size = 'C'

        def __init__(self, hair):
            self.hair = hair

        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

        @staticmethod
        def bio():
            print('全心全意为主人服务!')

        @classmethod
        def cls_name(cls):
            print('当前使用模具是{0}'.format(cls.__name__))

这次我将模具进行了一个小的改进,添加了一个名为cls_name的方法。因为我后续打算再做一些其他各种friend的模具(类),为了避免我弄混了不知道现在用的是哪个模具在生产,我加了这个方法。这个方法的作用是可以让程序显示出当前模具的名称,也就是代码class的名称。让我们看看如何使用:

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')     

    lili.cls_name()  # 显示:当前使用模具是Girlfriend
    Girlfriend.cls_name()  # 显示:当前使用模具是Girlfriend

从刚才的例子我们可以发现,用了两种不同的方式得到了同一个结果,有关于这点我们稍后再谈,先让我们回到开头,从开始说起。

当我们学习Python基础的时候,几乎所有的图书教程都会告诉我们一个知识点:Python为了内置的对象提供了一些内置的方法和属性,目的是为了更方便使用。简而言之,Python为class这个对象内置了一些方法和属性供我们直接使用,比如__name__和__dict__等等,我们已经谈到过是内置的名称前后就会都有双下划线,这里也是这样。在上面最新的模具中,我们应用了class的一个内置的name属性,而这个name记载了class的名称,所以用类似「class.__name__」这样的语法就能获得这个名称。

我们已经知道,如果对class里的方法(函数)不用@staticmethod装饰,调用的时候就会被程序硬塞一个实例作为第一个参数传入,用@staticmethod则调用的时候不隐式的传东西了,而现在我们的需求是查看显示模具本身的一个属性,跟实例无关,所以可以想到必须让方法能接收模具(类)而不是实例作为一个参数传入。到目前为止,我们学会了要么隐式传实例,要么啥也不传的手段,那么这种需要隐式传模具(类)可怎么办?Python早已考虑到了这种场景并准备了对应的装饰器供我们使用,没错,就是@classmethod。

可想而知,@classmethod就是让实例在调用被@classmethod装饰的方法时,隐式的传入模具(类)本身。类似self,由于约定俗称和class是一个Python关键字因而不能瞎用的原因,通常用「cls」这个英文作为指代类的参数名,这是Python里cls参数的由来。

@classmethod 的出场率也不高,但比@staticmethod用途更大,因为@classmethod 可以作为一种媒介,让我们更方便的操纵Python为class内置的一些属性和方法。那么,@classmethod 的应用场景也就不言而喻了。

@property

@property 是本文最后要谈及的一款装饰器了,让我们还是从改造模具开始:

# 定义类
    class Girlfriend:
        age = 22
        sex = '女'

        def __init__(self, hair):
            self.hair = hair
            self.bra = 'C'


        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

        @staticmethod
        def bio():
            print('全心全意为主人服务!')

        @classmethod
        def cls_name(cls):
            print('当前使用模具是{0}'.format(cls.__name__))

        @property
        def size(self):
            return self.bra

在刚才定义的最新的模具中,我做了这样几件事情:先将原来的属性size变成了一个同名方法,然后对其应用了@property装饰器,这些改变了什么?

在谈及@classmethod和之前的装饰器时,我们举例的模具在生产出女朋友之后,实际上都可以这么做:

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')     

    lili.size = 'D'  # 增大女朋友的罩杯
    lili.size  # 显示:D

也就是如果对女朋友的属性(胸)不满意,可以再调整。刚才的例子中我们对lili进行了「丰胸」,将lili原来的size由C升到了D。丰胸的过程是很简单的属性赋值语法,不用太多解释。但让我们用最新的定义了@property 装饰器的模具时,「丰胸」就失败了。在最新的模具中,我们可以使用「lili.size」得到C这个结果,但是无法再运行「lili.size = ‘D’」,一旦试图「丰胸」程序就会报错,报错信息大意是:不能写入size的属性值,原始报错信息如下:

Traceback (most recent call last):

File “<input>”, line 1, in <module>

AttributeError: can’t set attribute

那么过程和结果以及区别都看到了,但要理解@property还得从定义最新的模具开始说起。由于比较喜欢纯天然的妹纸,所以我对模具进行了改进,使得一旦生产出女朋友,则不能随意再去改变女朋友的size,并且只能是C。理解成对于代码的修改需求,就是让实例的size的值不能写入新的,但是可以读旧的,并且size仍旧要是一个属性。

Python也为我们考虑到了这种需求场景,并提供了@property 这个装饰器来满足这种需求,于是乎就有了本节最开始的新模具的改变。首先,我们注意到size由属性变成了一个方法,而基于我们Python的基础知识,对于Python方法的调用语法,都是类似「size()」这样方法(函数)名称后面加括号的形式,但就在刚才我们对新模具仍旧使用了「lili.size」并得到了结果,反而「lili.size()」无法运行(程序会报错),于是我们发现了@property的第一个作用:

@property 会将被装饰的类方法模拟成一个类属性

将原来是类的一个属性变成了这个类的方法,并且还行不更名坐不改姓,然后又将这个方法模拟成类属性一样可以调用,感觉这真是绕了好大一个圈。但我们并没有回到原点,因为既然是方法,那么就可以在方法里定义很多赋值语句无法完成的行为。但是我们举一反三之下紧接着有了一个疑问:不错,@property 把方法装饰成属性,就可以实现方法能做的,而原本的属性不能做的事情;那原来属性能做的事情,@property 装饰的方法是否也相应的不行了呢?

我们基础知识告诉我们,对于一个对象的属性,起码能做增、删、改、查这几件基本的事情,在前面最后的例子中,我们通过「lili.size」得到了女朋友的size属性,这是查询,但我们试图修改时,程序则报错了。看起来@property 将方法变成的属性并没有完整实现基本属性该有的几样功能,真是这样吗?让我们再继续改造一下模具,这是最后的一次改造:

    # 定义类
    class Girlfriend:
        age = 22
        sex = '女'

        def __init__(self, hair):
            self.hair = hair
            self.bra = 'C'

        def about_me(self):
            print('年芳{0},性别{1},身材{2}棒棒哒,一头{3}'.format(self.age, self.sex, self.size, self.hair))

        @staticmethod
        def bio():
            print('全心全意为主人服务!')

        @classmethod
        def cls_name(cls):
            print('当前使用模具是{0}'.format(cls.__name__))

        @property
        def size(self):
            return self.bra

        @size.setter
        def size(self, size):
            self.bra = size
            print('丰胸手术完成!')

在最后的一次改造中,我们增加了一个名为size()的方法,并对之使用了「@size.setter」这个装饰器。现在我们可以很容易的理解最新的size()方法干了些什么:先接收一个参数,并将这个参数赋给bra,这毫无疑问就是在丰胸。然后如果顺利,用print打印一个结果。在学习Python的基础时,我们已经知道同一命名空间下有两个重名方法是有问题的,会造成最后一个同名方法覆盖掉先前的那个。这里有两个size()方法让我们有些疑惑,@size.setter 又是什么呢?还是先试试:

    # 生产几个女朋友
    lili = Girlfriend('乌黑长发')
    mimi = Girlfriend('金黄短发')     

    lili.size  # 显示:C
    lili.size = 'D'  # 增大女朋友的罩杯,显示:丰胸手术完成!
    lili.size  # 显示:D

看起来丰胸手术已经可以顺利实施,与之前不同,程序不会再报错。可以联想到这一切一定是最新添加的那一小段代码的作用。

原来,Python不仅光提供@property 装饰器供你将一个方法变成属性调用,还提供了一系列配套的工具(setter、getter、deleter),这些正好对应增、删、改、查,这些工具让方法去更加完美的模拟属性的行为。这其中,当你用@property去装饰一个方法名为A的方法时,你可以再用@A.setter去装饰另一个同名方法。顾名思义,setter有写入的含义,那么@A.setter定义的就是方法A赋值时的行为。在上述的代码中,我们就定义了作为属性使用的size方法,在赋值时可以打印一个语句,提示「丰胸手术完成」。其余的几个工具就不多说,从字面意思就能理解做的事情,而使用方法都是一样的。所以,@property 和之后的工具使用,本质上都是针对@property装饰的方法做更多的定制,所以像上面的那样重名方法才不会有问题。

现在,你应该大致也理解了@property能干的事情,其作用和场景也很明显了。让我们在本文最后再看一段来自《Flask Web开发:基于Python的Web应用开发实战》P78,示例8-1的代码:

     class User(db.Model):
         password_hash = db.Column(db.String(128))

         @property
         def password(self):
             raise AttributeError('password is not a readable attribute')

         @password.setter
         def password(self, password):
             self.password_hash = generate_password_hash(password)

        def verify_password(self, password):
             return check_password_hash(self.password_hash, password)

这段代码中,作者使用了@property把原本是User的password方法变成属性一样可以调用。由于password是加密存储到数据库中的,于是需要一个方法完成加密过程,这正好可以由工具setter去干,于是乎我们看到@password.setter又装饰了一个password方法,并且这个方法我们能够看出是调用generate_password_hash()把原来的密码进行了加密,并将加密后的内容传给了password_hash这个属性,这个属性就是数据库中的用户密码字段。

Linux 常用命令总结

网上找了不少 Linux 方面的资料,令我想不通的是,这么简单的东西,为什么可以写的这么复杂。。我本地装了一个 Ubuntu 虚拟机,云上装了一个 CentOS,用着用着就熟练了

Linux 的命令格式

[root@localhost~]#

  • root: 当前登录用户
  • localhost: 主机名
  • ~ : 当前所在目录
  • : 超级用户的提示符
  • $ : 普通用户的提示符
  • / 开头的是绝对路径
  • . 是当前目录,统计目录可以省略 ./
  • .. 当前目录的父目录
  • cd 更改当前目录到家目录
  • cd – 更改当前目录到先前的目录
  • 每个用户都有一个家目录,默认在 /home/用户名
  • root 用户的家目录是 /root

文件命令

常规

  • 命令格式 [选项] [参数]
  • tap 自动补全命令
  • 打印当前工作目录: pwd
  • 查看文件类型 file
  • 查询目录中内容 ls [选项] [文件或目录]
    • ls -l 显示所有文件
    • ls -lh 人性化显示所有文件
    • ls -a 显示所有文件 以.开头的都是隐藏文件
  • cd (change direcotry):
    • 更改工作目录到先前的工作目录
  • cp (copy):
    • cp a.txt b.txt
    • 复制 a.txt 并把新文件命名为 b.txt
    • 复制目录要加上 -r 参数 cp -r a b
  • mkdir (创建一个目录)
    • -p 可以一次性创建多层目录
    • mkdir -p a/b/c
  • rmdir (remove empty directories) 删除一个空目录
  • rm
    • 这个命令直接删除东西,很危险,一般不要用
    • 删除文件或者目录
    • -f 强制删除
    • -r 用来删除目录
  • mv (用来移动文件\文件夹 或者改名)
    • mv a.txt b.txt 此为改名
    • mv b.txt ../
    • mv b.txt ../gua.txt
    • 可以用 mv xx /tmp 的方式来将文件放入临时文件夹(/tmp 是操作系统提供的临时文件夹,重启会删除里面的所有文件,可以替代 rm 删除命令)
  • cat 显示文件内容
  • tac 反过来显示文件内容
  • nl 显示内容并附带行号
  • more(分屏分批看文件内容)
  • less(比 more 好用,可以前后退看文件)
  • head,tail (显示文件的前,后10行)
  • head 和 tail 有一个 -n 参数 head-n 20 a.txt 显示20行
  • touch a.txt 如果文件存在就更新修改时间,不存在就创建文件

文件解压缩

  • tar -cf name.tar name 压缩
  • tar -tf name.tar 显示压缩的文件 tar -tvf name.tar 显示详细信息(都是只显示,不解压。v表示显示详细信息)verbose(详细)
  • tar -xf name.tar 解压(抽取)
  • tar -czvf name.tar.gz name (加了一个 z,以gz格式压缩)
  • tar -tzvf name.tar.gz (加了一个 z,以gz格式查看)
  • tar -xzvf name.tar.gz (加了一个 z,以gz格式抽取)
  • tar -cvf|tvf|xvf tar -czvf|tzvf|xzvf 记住这一行就行了
  • compression(create)listextract(提取)

文件编辑(vim)

  • touch 可以新建文件,vim 也可以直接新建文件
  • 打开后,写入内容,按 i 键(insert)
  • 退出 按 esc :wq 保存退出
  • 移到第一行 gg;移到最后一行 G
  • 删除一整行 dd;恢复 u。前提是从 insert 模式中切换出来
  • 复制一整行 yy;粘贴 p
  • 文件的权限 r-w-x 4-2-1 读-写-可执行

权限操作

  • sudo 用管理员账户执行程序(安装程序或修改一些系统配置都需要管理员权限)
  • su (switch user) 切换用户 su root
  • ll 全部显示

信息查找

  • file 显示文件类型
  • uname 显示操作系统名字或者其它信息 -r(内核) -a(全部)
  • whoami 查看当前身份
  • find . -name “*.py” 查找所有以 .py 结尾的文件

符号

  • ~ 家目录快捷方式
  • > 覆盖式重定向
  • >> 追加重定向

网络

  • ifconfig(查看 ip 一般使用这个)
  • ip addr(也可查看 ip)

SSH

  • ssh,安全外壳协议,建立在应用层,专为远程登录会话和其它网络服务提供安全性的协议
  • 以下内容在 CentOs 中操作,Ubuntu 中相关文件位置自行 Google

服务器安装 SSH 服务

  • 安装 yum install openssh-server
  • 启动 service sshd start
  • 设置开机运行 chkconfig sshd on
  • 服务器版本的操作系统一般都已经装好。桌面版的可能没有

客户端安装 SSH 工具

  • windows下很多工具支持,Xshell,Putty,secureCRT
  • Linux平台安装 yum install openssh-clients
  • 客户端连接SSH服务 ssh root@公网ip 实际与新建会话是一样的+
  • exit 回到本地

SSH config(配置)

  • 以下讲的都是linux客户端的config),客户端的config才能对自己生效
  • config 方便管理员批量管理多个 ssh
  • config 存放在 ~/.ssh/config(.ssh目录下没有的话就touch config新建一个)
  • SSH config 语法关键字:
    • Host 别名
    • HostName 主机名
    • Port 端口(ssh服务的默认端口为22)
    • User 用户名
    • IdentityFile 密钥文件的路径
    • 配置了config之后,连接服务器直接 ssh 别名 “` host “tencent” HostName xxx.xx.x.xx User root Port 22

### SSH安全免密码登录:ssh  key

+ 普通登录:`ssh username@xxx.xx.x.xx`
+ ssh key 使用非对称加密方式生成公钥和私钥
+ 私钥存放在本地 `~/.ssh` 目录
+ 公钥可以对外公开,放在服务器的 `~/.ssh/authorized_keys` 
+ Linux 平台生成 ssh key:
	+ 客户端cd ~/.ssh/
	+ 客户端使用 ssh-keygen -t rsa 命令亦可 ssh-keygen -t dsa
	+ 服务端在 authorized_keys 文件中写入客户端生成的公钥
	+ 客户端将密钥加载到ssh服务中:`ssh-add ~/.ssh/私钥文件`
+ windows 平台生成 ssh key:
	+ 直接在 xshell 工具栏中生成
	+ 在 ~/.ssh 目录下建立 authorized_keys 文件,编辑放入公钥
+ 如果是新用户,则还要修改一下权限

cd /home/username/ chmod 700 .ssh cd /home/username/.ssh chmod 600 authorized_keys


### SSH安全端口

+ 避免服务器的远程连接端口被别人知道
+ 改变 SSH 服务端口:修改 `/etc/ssh/sshd_config`(里面也可以添加连接的端口,这是对服务器端的操作)

## 软硬件的安装、查看

### 软件操作(CentOs)

+ 软件包管理器: yum
+ 安装软件: `yum intall xxx`
+ 卸载软件: `yum remove xxx`
+ 搜索软件: `yum serach xxx`
+ 清理缓存: `yum clean packages`
+ 列出已安装: `yum list`
+ 软件包信息: `yum info xxx`

### 软件操作(Ubuntu)

+ `sudo apt-get install name` `sudo apt install`
+ 备份Ubuntu默认源地址 `sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup`
+ 更新源服务器列表,即 `/etc/apt/sources.list`
+ `sudo apt-get update` sudo apt install 
+ `sudo apt-get remove name` 删除包
+ `sudo apt-cache search package` 搜索软件包
+ `sudo apt-cache show package`  获取包的相关信息,如说明、大小、版本等

### 硬件操作

+ 内存: `free -m`
+ 硬盘:`df -h`
+ 负载: `w/top`
+ cpu: `cat /proc/cpuinfo`

## 其它

### kill

+ `sudo netstat -tnlp` 查看所有进程端口占用情况
+ `sudo kill xxpidxx` 杀死进程

### 防火墙

+ `yum install firewalld`				安装
+ `service firewalld start`			启动
+ `service firewalld status`			检查状态
+ `service firewalld stop/disable` 	关闭/禁用防火墙

+ 可能已默认安装,用 `yum list |grep firewall` 检查

+ `firewall-cmd --version` 	查看版本
+ `firewall-cmd --help`		查看帮助文档
+ `firewall-cmd --state`		查看运行状态

> 在防火墙有区域和端口的区分

+ `firewall-cmd --get-zones` 查看区域
+ `firewall-cmd --get-default-zone` 默认区域
+ `firewall-cmd --list-all-zone` 列出每一个区域的配置信息
+ `firewall-cmd --query-service=ssh` 查询服务
+ `firewall-cmd --remove-serivce=ssh` 删除服务
+ `firewall-cmd --add-service=ssh` 添加服务
+ `firewall-cmd --list-services` 列出所有服务
+ `firewall-cmd --query-port=22/tcp` 查询端口是否开启
+ `firewall-cmd --add-port=22/tcp` 开启端口
+ `firewall-cmd --remove-port=22/tcp` 关闭端口
+ `firewall-cmd --list-ports` 查看所有打开的端口

+ 端口和服务的概念
+ 删除服务后开启端口依然可以(已实测)
+ 安装了服务都会开启默认端口,不用特意操作。测试时关闭防火墙

### 提权 、上传、下载

+ 提权:sudo
+ root 下通过 visudo 命令将用户加入到文件中

Allows people in group wheel to run all commands

%wheel ALL=(ALL) ALL %rong ALL=(ALL) ALL “`

  • 一般不用 root 进行操作。都是将用户加入文件,用用户操作,特殊命令用 sudo 提权
  • 文件下载
    • wget wget http://www.baidu.com
    • curl curl -o filename http://www.baidu.com 将下载下来的文件命名为 filename
  • 文件上传
    • scp上传文件格式为 scp 文件名 用户@公网地址:路径 例:scp a.txt rong@192.168.x.xxx:/tmp/ (scp是 secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令)
    • 下载 scp rong@192.168.x.xxx:/tmp/a.txt ./ 格式为:scp 用户名@公网地址:服务器上文件地址 文件要保存的本机地址以上是 linux 下的操作,windows 下见下
    • 服务器上安装软件 yum install lrzsz(在linux里可代替ftp上传和下载)
    • 服务器端 rz 上传命令(服务器端 receive)
    • 服务器端 sz filename 从服务器端下载文件(服务器端 send)
    • ZMODEM 协议

高效能人士的七个习惯

习惯一:积极主动(个人愿景的原则)

人性的本质是主动而非被动地——人类确实能主动努力以提升生命价值(主观能动性)

积极主动,不仅指行事的态度,还意味着为人一定要对自己的人生负责

积极主动的人专注于“影响圈”,专心做自己力所能及的事情,能量是积极的,是影响圈不断扩大

影响圈的核心就是,作出承诺与信守诺言的能力

这是其他习惯的基础

习惯二: 以终为始(自我领导的原则)

以你的人生目标作为衡量一切的标准。由个人最重视的期许或价值观来决定一切

在做任何事情之前,都需要认清方向——定目标

你以什么为中心?你为谁而活?

以原则为中心

以永恒不变的原则作为生活重心,就能建立高效能的思维定势,也就能正确审视所有其他的生活中心。(一个人的思维定式能决定他的态度和行为)

原因:主动的选择,没有受到环境和他人的影响;最有效的选择,且长期可预料;基于原则所作出的选择,能提高自身的价值。

所以,撰写使命宣言并付诸实践(确定角色和目标)

书写个人使命宣言 —— 即人生哲学或基本信念 (个人宪法,基于正确原则的个人使命宣言也同样是评价一切的标准)

习惯三:要事第一(自我管理的原则)

把最重要的事情放在第一位,先做重要的事情

矩阵: 重要/不重要 * 紧急/不紧急

关注重要不紧急的事情

重要不紧急的事情: 建立人际关系,撰写使命宣言,规划长期目标,防患于未然等等

在必要时,勇于说“不”

学会授权

习惯四:双赢思维(人际领导的原则)

双赢 – 不断地在人际交往中寻求双边利益

要明确意识到其重要性

基础:信任

双赢关系

双赢的精髓就是信用,即情感账户

双赢过程: + 要从对方的角度看问题,真正理解对方的想法 + 认清主要问题和顾虑(而非立场) + 确定大家都能接受的结果 + 实现这种结果的各种可能途径

习惯五:知己知彼(移情沟通的原则)

首先寻求去了解对方,然后再争取让对方了解自己。(进行有效人际交流的关键)

你真的听懂了么?

移情聆听

以理解为目的的聆听,要求听者站在说话者的角度理解他们的思维模式和感受

不仅要耳到,还要眼到,心到。用眼睛去观察,用心灵去体会

有效的沟通: + 第一阶段,复述语句,至少能使人专心聆听 + 第二阶段,加入解释,纯用自己的词句表达,用左脑的逻辑思考去理解 + 第三阶段,深入个人的感觉,右脑发挥作用,开始体会对方的心情 + 即加以理解,又带有感情,左右脑并用

如果你真正爱一个人,那么花时间了解对方将有益于今后的坦诚相待

习惯六 :统合综效(创造性合作的原则)

统合综效的基本心态是——如果一位具有相当聪明才智的人跟我意见不同,那么对方的主张必定有我尚未体会的奥妙,值得加以理解

有分歧才有收获

统合综效就是整体大于部分之和

统合综效的精髓就是判断和尊重差异,取长补短

所谓统合综效的沟通,是指敞开胸怀,接纳一切奇怪的想法,同时也贡献自己的浅见

尊重差异,要尊重,不偏激

即使处于不利的境地,也不应该放弃追求统合综效

习惯七:不断更新(平衡的自我更新原则)

拥有财富,并不代表经济独立,拥有创造财富的能力才真正可靠

自我提升和完善的四个层面:身体,精神,智力,社会/情感

不断更新,意味着要兼顾这四种要素

身体层面:健康饮食、充足休息以及定期锻炼(耐力、人性、力量)

精神层面:人的本质,核心,对价值体系的坚持,是生活中非常私人但是至关重要的领域

智力层面:主要靠教育,借此不断学习知识,魔力心智,开阔视野;养成定期阅读优秀文学作品的习惯;另一种有效的方式是写作,不断记录自己的想法,经历深刻见解和学习心得

社会、情感层面:在与他人的日常交往中完成,还需要必要的练习;坚守原则,肯定自我,与人为善,相信人生不止输赢两种抉择;平衡,要把握平衡;螺旋式上升,良性循环,学习-坚持-时间,并沿着螺旋式上升的路线不断提高实践的层次

ORM,操作数据库

一 ORM是什么?为何要有ORM?

​ 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(增、删、改、查),而一旦谈到数据的管理操作,就需要用到数据库管理软件,例如mysql、oracle、Microsoft SQL Server等。

如果应用程序需要操作数据(比如将用户注册信息永久存放起来),那么我们需要在应用程序中编写原生sql语句,然后使用pymysql模块远程操作mysql数据库

针对应用程序的数据操作,直接编写原生sql语句会存在两方面的问题,严重影响开发效率,如下

#1. sql语句的执行效率问题:应用开发程序员需要耗费一大部分精力去优化sql语句
#2. 数据库迁移问题:针对mysql开发的sql语句无法直接应用到oracle数据库上,一旦需要迁移数据库,便需要考虑跨平台问题

为了解决上述问题,django引入了ORM的概念,ORM全称Object Relational Mapping,即对象关系映射,是在pymysq之上又进行了一层封装,对于数据的操作,我们无需再去编写原生sql,取代代之的是基于面向对象的思想去编写类、对象、调用相应的方法等,ORM会将其转换/映射成原生SQL然后交给pymysql执行

​ 有了ORM框架,开发人员既不用再去考虑原生SQL的优化问题,也不用考虑数据库迁移的问题,ORM都帮我们做了优化且支持多种数据库,这极大地提升了我们的开发效率

如何使用:
1、数据来源于数据库的表,而ORM的模型类对应数据库表,所以若我们想操作数据,必须先创建模型。
2、再注册到应用
3、再配置数据库连接
以下是一个 mysql的数据库连接配置,包含用的pymysql库,用户名zwq,密码5dc580,服务器zxz.xtaa.cn,数据库名douy
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://zwq:5dc580@zxz.xtaa.cn/douyin?charset=utf8"







Flask是一个轻量级的Web框架,其中集成的ORM工具SQLAlchemy,使得在Python中进行数据库操作变得更加简单。在Flask中,使用ORM可以通过使用db.session对象访问数据库,将操作转换为Python对象的方法。 在ORM中的一个核心概念是关系,即对象之间的关系。在Flask中,使用db.relationship来创建关系。本文将通过几个方面详细介绍db.relationship的使用。
一、创建一对多关系
在ORM中,一对多关系是指一个对象(如一个用户)可以有多个子对象(如多个订单)。在Flask中,可以通过db.relationship和backref参数来定义一对多关系。在这种关系中,一个对象(如User)可以有多个子对象(如Order),而一个子对象(如Order)只能归属于一个对象(如User)。

 class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(50))
        orders = db.relationship("Order", backref="user")

    class Order(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        total_price = db.Column(db.Float)
        user_id = db.Column(db.Integer, db.ForeignKey("user.id"))

    user = User.query.first()
    orders = user.orders
    for order in orders:
        print(order.total_price)

在这个示例中,User和Order模型之间建立了一对多关系。User模型中通过db.relationship定义了一个名为orders的属性。在Order模型中通过user_id列的外键来关联User模型。同时,backref参数定义了子对象如何访问父对象。在这个示例中,我们可以通过user.orders访问该用户的所有订单,并通过for循环遍历订单对象,打印其总价。

二、创建多对多关系

多对多关系是指两个对象之间相互关联,一个对象可以有多个子对象,同时一个子对象也可以归属于多个对象。在Flask中,可以通过db.relationship和secondary参数来定义多对多关系。在这种关系中需要创建一个关联表,用来保存多对多的关系。

association_table = db.Table(
        'association', db.Model.metadata,
        db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
        db.Column('project_id', db.Integer, db.ForeignKey('project.id')))

    class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(50))
        projects = db.relationship("Project", secondary=association_table, backref="users")

    class Project(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(50))

    user = User.query.first()
    projects = user.projects
    for project in projects:
        print(project.name)

在这个示例中,User和Project模型之间建立了多对多关系。User模型中定义了名为projects的属性。在Project模型中同样定义了名为users的属性来反向访问父对象。关联表association_table通过db.Table来创建。在User模型中通过secondary参数来指定关联表。在示例中,我们可以通过user.projects访问该用户所参与的所有项目,并通过for循环遍历打印每个项目的名字。

三、定义关系属性的限制

在ORM中,还可以为关系属性定义一些限制,如elenment_order、primaryjoin、secondaryjoin等等。这些限制可以帮助我们更好地控制我们的关系。例如,为了控制查询结果,我们可以添加primaryjoin参数

四、使用back_populates来反向引用一对多关系

在Flask中,我们可以使用back_populates来反向引用一对多关系。这种方式可以帮助我们更好地控制关系,并避免循环引用

alist

https://github.com/alist-org/alist

有windows和liunx版本,可加入多种网盘,还可配合aria2等来离线下载。

如果要让别人查看的话在后台里把来宾帐户启用,再把无需密码访问选上。

安装后可以更改去掉相关特征信息

在设置-全局-自定义头部

<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<style>
.footer {
    display: none!important;
}
body:before {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: .3;
    z-index: -1;
    display: block;
    position: fixed;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center center;
    background-attachment: fixed;
    content: '';
    background-image: url(https://oss.iymark.com/2022/09/vback.jpg);
}
.hope-c-PJLV-ikSuVsl-css {
  background: none!important;
}
.markdown-body a {
  color: #000!important;
}
.hope-ui-dark .markdown-body a {
  color: #fff!important;
}
.copyright .link {
    padding: 4px;
    background: #26517c;
    border-radius: 0 8px 8px 0;
}
.copyright .name {
    padding: 4px;
    background: #fff;
    border-radius: 8px 0 0 8px;
    color:#000;
}
.copyright {
    padding: 50px;
    background: #2d3236!important;
}
.copyright a {
  color: #fff;
}
br.phone, br.pad{
    display:none;
}
@media (max-width: 891px){
br.pad {
    display:block;
}
}
@media (max-width: 561px){
br.phone {
    display:block;
}
}
.hope-c-PJLV-ieESZju-css {
    display: none;
}
img.hope-c-PJLV-ibwASZs-css {
    width: auto;
}
.hope-ui-dark .copyright .name {
    background: #000;
}
.runtime {
    margin-top: 20px;
    color: #fff;
    text-align: right!important;
}
.about, .state {
    width: min(99%, 980px);
    text-align: center;
    padding-inline: 2%;
}
.state {
    margin-top: 20px;
    color:#fff;
}
</style>

在自己定义内容里

<div class="copyright" align="center"><div class="about"><p>
<span class="name">© 2020-2023</span><span class="link"><a href="https://www.xtaa.cn">奇言</a></span><br class="phone"><br class="phone">
</p>
<div class="runtime">
<span id="runtime_span"></span>
<script type="text/javascript">
    function show_runtime() {
        window.setTimeout("show_runtime()", 1000);
        X = new Date("2/24/2023 00:00:00");
        Y = new Date();
        T = (Y.getTime() - X.getTime());
        M = 24 * 60 * 60 * 1000;
        a = T / M;
        A = Math.floor(a);
        b = (a - A) * 24;
        B = Math.floor(b);
        c = (b - B) * 60;
        C = Math.floor((b - B) * 60);
        D = Math.floor((c - C) * 60);
        runtime_span.innerHTML = "<span class=\"name\">稳定运行" + A + "天</span><span class=\"link\">" + B + "时" + C + "分" + D + "秒</span>"
    }
    show_runtime();
</script>
</div>
</div>
<div class="state"><p>免责声明:本站为个人资源库,资源库所发布的一切影视、源代码、注册信息及软件等资源仅限用于学习和研究目的</p></div>
</div>




另外可容器直接安装已挂载大量内容的小雅alist:
http://alist.xiaoya.pro/
一键安装和更新容器,标准模式,打开端口 5678
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)"

一键安装和更新容器,host模式(推荐,软路由和NAS上更少网络故障,打开端口 6789)
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)" -s host